From 2d2a3f14dd3c598d21f632a71c47de251fa1f02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 15 Aug 2024 09:08:06 +0200 Subject: [PATCH 01/78] Started a rewrite of the property editor The new property editor will be based on a widget hierarchy instead of a QTreeView. This will allow better customization and direct widget interaction. This first step implements various property edit widgets which are instantiated based on the type of QVariant values. * DoubleSpinBox, with precision logic from existing property editor * Custom spin boxes and combo boxes which shrink horizontally * Property names elide when there isn't enough space * QPointF and QColor editor factories * Includes a test with all built-in map properties * Supports headers (not collapsible for now) * Supports separators * EnumEditorFactory, with support for non-consecutive enums * Name and editor widgets are stretched by a fixed 2:3 ratio and ignore their size hint * Includes some start on possible API for setting/getting the value --- src/tiled/libtilededitor.qbs | 2 + src/tiled/propertiesdock.cpp | 12 +- src/tiled/propertiesdock.h | 4 +- src/tiled/varianteditor.cpp | 680 +++++++++++++++++++++++++++++++++++ src/tiled/varianteditor.h | 82 +++++ 5 files changed, 772 insertions(+), 8 deletions(-) create mode 100644 src/tiled/varianteditor.cpp create mode 100644 src/tiled/varianteditor.h diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index b31c299cf1..5e13f8bef3 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -553,6 +553,8 @@ DynamicLibrary { "undodock.h", "utils.cpp", "utils.h", + "varianteditor.cpp", + "varianteditor.h", "varianteditorfactory.cpp", "varianteditorfactory.h", "variantpropertymanager.cpp", diff --git a/src/tiled/propertiesdock.cpp b/src/tiled/propertiesdock.cpp index 36cde21f64..f47e2e10f0 100644 --- a/src/tiled/propertiesdock.cpp +++ b/src/tiled/propertiesdock.cpp @@ -20,7 +20,7 @@ #include "propertiesdock.h" -#include "propertieswidget.h" +#include "varianteditor.h" #include @@ -28,26 +28,26 @@ namespace Tiled { PropertiesDock::PropertiesDock(QWidget *parent) : QDockWidget(parent) - , mPropertiesWidget(new PropertiesWidget(this)) + , mPropertiesWidget(new VariantEditor(this)) { setObjectName(QLatin1String("propertiesDock")); setWidget(mPropertiesWidget); - connect(mPropertiesWidget, &PropertiesWidget::bringToFront, - this, &PropertiesDock::bringToFront); + // connect(mPropertiesWidget, &PropertiesWidget::bringToFront, + // this, &PropertiesDock::bringToFront); retranslateUi(); } void PropertiesDock::setDocument(Document *document) { - mPropertiesWidget->setDocument(document); + // mPropertiesWidget->setDocument(document); } void PropertiesDock::selectCustomProperty(const QString &name) { bringToFront(); - mPropertiesWidget->selectCustomProperty(name); + // mPropertiesWidget->selectCustomProperty(name); } bool PropertiesDock::event(QEvent *event) diff --git a/src/tiled/propertiesdock.h b/src/tiled/propertiesdock.h index c39532484d..e5e96dbdd7 100644 --- a/src/tiled/propertiesdock.h +++ b/src/tiled/propertiesdock.h @@ -26,7 +26,7 @@ namespace Tiled { class Document; -class PropertiesWidget; +class VariantEditor; class PropertiesDock : public QDockWidget { @@ -50,7 +50,7 @@ public slots: void bringToFront(); void retranslateUi(); - PropertiesWidget *mPropertiesWidget; + VariantEditor *mPropertiesWidget; }; } // namespace Tiled diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp new file mode 100644 index 0000000000..d2bc1f7327 --- /dev/null +++ b/src/tiled/varianteditor.cpp @@ -0,0 +1,680 @@ +/* + * varianteditor.cpp + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "varianteditor.h" +#include "colorbutton.h" +#include "compression.h" +#include "map.h" +#include "tiled.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Tiled { + +class SpinBox : public QSpinBox +{ + Q_OBJECT + +public: + SpinBox(QWidget *parent = nullptr) + : QSpinBox(parent) + { + // Allow the full range by default. + setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + + // Allow the widget to shrink horizontally. + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + } + + QSize minimumSizeHint() const override + { + // Don't adjust the horizontal size hint based on the maximum value. + auto hint = QSpinBox::minimumSizeHint(); + hint.setWidth(Utils::dpiScaled(50)); + return hint; + } +}; + +/** + * Strips a floating point number representation of redundant trailing zeros. + * Examples: + * + * 0.01000 -> 0.01 + * 3.000 -> 3.0 + */ +QString removeRedundantTrialingZeros(const QString &text) +{ + const QString decimalPoint = QLocale::system().decimalPoint(); + const auto decimalPointIndex = text.lastIndexOf(decimalPoint); + if (decimalPointIndex < 0) // return if there is no decimal point + return text; + + const auto afterDecimalPoint = decimalPointIndex + decimalPoint.length(); + int redundantZeros = 0; + + for (int i = text.length() - 1; i > afterDecimalPoint && text.at(i) == QLatin1Char('0'); --i) + ++redundantZeros; + + return text.left(text.length() - redundantZeros); +} + +class DoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT + +public: + DoubleSpinBox(QWidget *parent = nullptr) + : QDoubleSpinBox(parent) + { + // Allow the full range by default. + setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + + // Increase possible precision. + setDecimals(9); + + // Allow the widget to shrink horizontally. + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + } + + QSize minimumSizeHint() const override + { + // Don't adjust the horizontal size hint based on the maximum value. + auto hint = QDoubleSpinBox::minimumSizeHint(); + hint.setWidth(Utils::dpiScaled(50)); + return hint; + } + + // QDoubleSpinBox interface + QString textFromValue(double val) const override + { + auto text = QDoubleSpinBox::textFromValue(val); + + // remove redundant trailing 0's in case of high precision + if (decimals() > 3) + return removeRedundantTrialingZeros(text); + + return text; + } +}; + + +// A label that elides its text if there is not enough space +class ElidingLabel : public QLabel +{ + Q_OBJECT + +public: + explicit ElidingLabel(QWidget *parent = nullptr) + : ElidingLabel(QString(), parent) + {} + + ElidingLabel(const QString &text, QWidget *parent = nullptr) + : QLabel(text, parent) + { + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + } + + QSize minimumSizeHint() const override + { + auto hint = QLabel::minimumSizeHint(); + hint.setWidth(std::min(hint.width(), Utils::dpiScaled(30))); + return hint; + } + + void paintEvent(QPaintEvent *) override + { + const int m = margin(); + const QRect cr = contentsRect().adjusted(m, m, -m, -m); + const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; + const int align = QStyle::visualAlignment(dir, alignment()); + const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight + : Qt::TextForceRightToLeft); + + QStyleOption opt; + opt.initFrom(this); + + const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); + + const bool isElided = elidedText != text(); + if (isElided != m_isElided) { + m_isElided = isElided; + setToolTip(isElided ? text() : QString()); + } + + QPainter painter(this); + QWidget::style()->drawItemText(&painter, cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); + } + +private: + bool m_isElided = false; +}; + +// A label that matches its preferred height with that of a line edit +class LineEditLabel : public ElidingLabel +{ + Q_OBJECT + +public: + using ElidingLabel::ElidingLabel; + + QSize sizeHint() const override + { + auto hint = ElidingLabel::sizeHint(); + hint.setHeight(lineEdit.sizeHint().height()); + return hint; + } + +private: + QLineEdit lineEdit; +}; + +class StringEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QLineEdit(parent); + editor->setText(value.toString()); + return editor; + } + + QVariant value(QWidget *editor) const override + { + return static_cast(editor)->text(); + } +}; + +class IntEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new SpinBox(parent); + editor->setValue(value.toInt()); + return editor; + } +}; + +class FloatEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new DoubleSpinBox(parent); + editor->setValue(value.toDouble()); + return editor; + } +}; + +class BoolEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QCheckBox(parent); + bool checked = value.toBool(); + editor->setChecked(checked); + editor->setText(checked ? tr("On") : tr("Off")); + + QObject::connect(editor, &QCheckBox::toggled, [editor](bool checked) { + editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); + }); + + return editor; + } +}; + +class PointEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto horizontalLayout = new QHBoxLayout(editor); + horizontalLayout->setContentsMargins(QMargins()); + + auto xLabel = new QLabel(QStringLiteral("X"), editor); + horizontalLayout->addWidget(xLabel, 0, Qt::AlignRight); + + auto xSpinBox = new SpinBox(editor); + xLabel->setBuddy(xSpinBox); + horizontalLayout->addWidget(xSpinBox, 1); + + auto yLabel = new QLabel(QStringLiteral("Y"), editor); + horizontalLayout->addWidget(yLabel, 0, Qt::AlignRight); + + auto ySpinBox = new SpinBox(editor); + yLabel->setBuddy(ySpinBox); + horizontalLayout->addWidget(ySpinBox, 1); + + // horizontalLayout->addStretch(); + + xSpinBox->setValue(value.toPoint().x()); + ySpinBox->setValue(value.toPoint().y()); + + return editor; + } +}; + +class PointFEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto horizontalLayout = new QHBoxLayout(editor); + horizontalLayout->setContentsMargins(QMargins()); + + auto xLabel = new QLabel(QStringLiteral("X"), editor); + horizontalLayout->addWidget(xLabel, 0, Qt::AlignRight); + + auto xSpinBox = new DoubleSpinBox(editor); + xLabel->setBuddy(xSpinBox); + horizontalLayout->addWidget(xSpinBox, 1); + + auto yLabel = new QLabel(QStringLiteral("Y"), editor); + horizontalLayout->addWidget(yLabel, 0, Qt::AlignRight); + + auto ySpinBox = new DoubleSpinBox(editor); + yLabel->setBuddy(ySpinBox); + horizontalLayout->addWidget(ySpinBox, 1); + + // horizontalLayout->addStretch(); + + xSpinBox->setValue(value.toPointF().x()); + ySpinBox->setValue(value.toPointF().y()); + + return editor; + } +}; + +class SizeEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto horizontalLayout = new QHBoxLayout(editor); + horizontalLayout->setContentsMargins(QMargins()); + + auto widthLabel = new QLabel(QStringLiteral("W"), editor); + widthLabel->setToolTip(tr("Width")); + horizontalLayout->addWidget(widthLabel, 0, Qt::AlignRight); + + auto widthSpinBox = new SpinBox(editor); + widthLabel->setBuddy(widthSpinBox); + horizontalLayout->addWidget(widthSpinBox, 1); + + auto heightLabel = new QLabel(QStringLiteral("H"), editor); + heightLabel->setToolTip(tr("Height")); + horizontalLayout->addWidget(heightLabel, 0, Qt::AlignRight); + + auto heightSpinBox = new SpinBox(editor); + heightLabel->setBuddy(heightSpinBox); + horizontalLayout->addWidget(heightSpinBox, 1); + + // horizontalLayout->addStretch(); + + widthSpinBox->setValue(value.toSize().width()); + heightSpinBox->setValue(value.toSize().height()); + + return editor; + } +}; + +class RectFEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto gridLayout = new QGridLayout(editor); + gridLayout->setContentsMargins(QMargins()); + gridLayout->setColumnStretch(4, 1); + + auto xLabel = new QLabel(QStringLiteral("X"), editor); + gridLayout->addWidget(xLabel, 0, 0, Qt::AlignRight); + + auto xSpinBox = new DoubleSpinBox(editor); + xLabel->setBuddy(xSpinBox); + gridLayout->addWidget(xSpinBox, 0, 1); + + auto yLabel = new QLabel(QStringLiteral("Y"), editor); + gridLayout->addWidget(yLabel, 0, 2, Qt::AlignRight); + + auto ySpinBox = new DoubleSpinBox(editor); + yLabel->setBuddy(ySpinBox); + gridLayout->addWidget(ySpinBox, 0, 3); + + auto widthLabel = new QLabel(QStringLiteral("W"), editor); + widthLabel->setToolTip(tr("Width")); + gridLayout->addWidget(widthLabel, 1, 0, Qt::AlignRight); + + auto widthSpinBox = new DoubleSpinBox(editor); + widthLabel->setBuddy(widthSpinBox); + gridLayout->addWidget(widthSpinBox, 1, 1); + + auto heightLabel = new QLabel(QStringLiteral("H"), editor); + heightLabel->setToolTip(tr("Height")); + gridLayout->addWidget(heightLabel, 1, 2, Qt::AlignRight); + + auto heightSpinBox = new DoubleSpinBox(editor); + heightLabel->setBuddy(heightSpinBox); + gridLayout->addWidget(heightSpinBox, 1, 3); + + const auto rect = value.toRectF(); + xSpinBox->setValue(rect.x()); + ySpinBox->setValue(rect.y()); + widthSpinBox->setValue(rect.width()); + heightSpinBox->setValue(rect.height()); + + return editor; + } +}; + +class ColorEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new ColorButton(parent); + editor->setColor(value.value()); + return editor; + } +}; + +class EnumEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new QComboBox(parent); + // This allows the combo box to shrink horizontally. + editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + editor->setModel(&m_enumNamesModel); + + if (m_enumValues.isEmpty()) + editor->setCurrentIndex(value.toInt()); + else + editor->setCurrentIndex(m_enumValues.indexOf(value.toInt())); + + return editor; + } + + void setEnumNames(const QStringList &enumNames) + { + m_enumNamesModel.setStringList(enumNames); + } + + void setEnumValues(const QList &enumValues) + { + m_enumValues = enumValues; + } + +private: + QStringListModel m_enumNamesModel; + QList m_enumValues; +}; + + +VariantEditor::VariantEditor(QWidget *parent) + : QScrollArea(parent) +{ + m_widget = new QWidget; + auto verticalLayout = new QVBoxLayout(m_widget); + m_gridLayout = new QGridLayout; + verticalLayout->addLayout(m_gridLayout); + verticalLayout->addStretch(); + verticalLayout->setContentsMargins(0, 0, 0, 0); + + setWidget(m_widget); + setWidgetResizable(true); + + m_gridLayout->setContentsMargins(0, 0, 0, 0); + m_gridLayout->setSpacing(Utils::dpiScaled(3)); + + m_gridLayout->setColumnStretch(LabelColumn, 2); + m_gridLayout->setColumnStretch(WidgetColumn, 3); + m_gridLayout->setColumnMinimumWidth(LeftSpacing, Utils::dpiScaled(3)); + m_gridLayout->setColumnMinimumWidth(MiddleSpacing, Utils::dpiScaled(2)); + m_gridLayout->setColumnMinimumWidth(RightSpacing, Utils::dpiScaled(3)); + + registerEditorFactory(QMetaType::QString, std::make_unique()); + registerEditorFactory(QMetaType::Int, std::make_unique()); + registerEditorFactory(QMetaType::Double, std::make_unique()); + registerEditorFactory(QMetaType::Bool, std::make_unique()); + registerEditorFactory(QMetaType::QPoint, std::make_unique()); + registerEditorFactory(QMetaType::QPointF, std::make_unique()); + registerEditorFactory(QMetaType::QSize, std::make_unique()); + registerEditorFactory(QMetaType::QRectF, std::make_unique()); + registerEditorFactory(QMetaType::QColor, std::make_unique()); + + auto alignmentEditorFactory = std::make_unique(); + alignmentEditorFactory->setEnumNames({ + tr("Unspecified"), + tr("Top Left"), + tr("Top"), + tr("Top Right"), + tr("Left"), + tr("Center"), + tr("Right"), + tr("Bottom Left"), + tr("Bottom"), + tr("Bottom Right"), + }); + registerEditorFactory(qMetaTypeId(), std::move(alignmentEditorFactory)); + + auto orientationEditorFactory = std::make_unique(); + orientationEditorFactory->setEnumNames({ + tr("Orthogonal"), + tr("Isometric"), + tr("Isometric (Staggered)"), + tr("Hexagonal (Staggered)"), + }); + orientationEditorFactory->setEnumValues({ + Map::Orthogonal, + Map::Isometric, + Map::Staggered, + Map::Hexagonal, + }); + registerEditorFactory(qMetaTypeId(), std::move(orientationEditorFactory)); + + auto staggerAxisEditorFactory = std::make_unique(); + staggerAxisEditorFactory->setEnumNames({ + tr("X"), + tr("Y"), + }); + registerEditorFactory(qMetaTypeId(), std::move(staggerAxisEditorFactory)); + + auto staggerIndexEditorFactory = std::make_unique(); + staggerIndexEditorFactory->setEnumNames({ + tr("Odd"), + tr("Even"), + }); + registerEditorFactory(qMetaTypeId(), std::move(staggerIndexEditorFactory)); + + QStringList layerFormatNames = { + QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), + }; + QList layerFormatValues = { + Map::XML, + Map::Base64, + Map::Base64Gzip, + Map::Base64Zlib, + }; + + if (compressionSupported(Zstandard)) { + layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); + layerFormatValues.append(Map::Base64Zstandard); + } + + layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); + layerFormatValues.append(Map::CSV); + + auto layerFormatEditorFactory = std::make_unique(); + layerFormatEditorFactory->setEnumNames(layerFormatNames); + layerFormatEditorFactory->setEnumValues(layerFormatValues); + registerEditorFactory(qMetaTypeId(), std::move(layerFormatEditorFactory)); + + auto renderOrderEditorFactory = std::make_unique(); + renderOrderEditorFactory->setEnumNames({ + tr("Right Down"), + tr("Right Up"), + tr("Left Down"), + tr("Left Up"), + }); + registerEditorFactory(qMetaTypeId(), std::move(renderOrderEditorFactory)); + + // setValue(QVariantMap { + // { QStringLiteral("Name"), QVariant(QLatin1String("Hello")) }, + // { QStringLiteral("Position"), QVariant(QPoint(15, 50)) }, + // { QStringLiteral("Size"), QVariant(QSize(35, 400)) }, + // { QStringLiteral("Rectangle"), QVariant(QRectF(15, 50, 35, 400)) }, + // { QStringLiteral("Margin"), QVariant(10) }, + // { QStringLiteral("Opacity"), QVariant(0.5) }, + // { QStringLiteral("Visible"), true }, + // { QStringLiteral("Object Alignment"), QVariant::fromValue(TopLeft) }, + // }); + + + // setValue(QVariantList { + // QVariant(QLatin1String("Hello")), + // QVariant(10), + // QVariant(3.14) + // }); + + addHeader(tr("Map")); + addValue(tr("Class"), QString()); + addSeparator(); + addValue(tr("Orientation"), QVariant::fromValue(Map::Hexagonal)); + addValue(tr("Infinite"), false); + addValue(tr("Map Size"), QSize(20, 20)); + addValue(tr("Tile Size"), QSize(14, 12)); + addValue(tr("Tile Side Length (Hex)"), 6); + addValue(tr("Stagger Axis"), QVariant::fromValue(Map::StaggerY)); + addValue(tr("Stagger Index"), QVariant::fromValue(Map::StaggerEven)); + addSeparator(); + addValue(tr("Parallax Origin"), QPointF()); + addSeparator(); + addValue(tr("Tile Layer Format"), QVariant::fromValue(Map::Base64Zlib)); + addValue(tr("Output Chunk Size"), QSize(16, 16)); + addValue(tr("Compression Level"), -1); + addSeparator(); + addValue(tr("Tile Render Order"), QVariant::fromValue(Map::RightDown)); + addValue(tr("Background Color"), QColor()); + addHeader(tr("Custom Properties")); +} + +void VariantEditor::registerEditorFactory(int type, std::unique_ptr factory) +{ + m_factories[type] = std::move(factory); +} + +void VariantEditor::addHeader(const QString &text) +{ + auto label = new ElidingLabel(text, m_widget); + auto boldFont = label->font(); + boldFont.setBold(true); + label->setFont(boldFont); + label->setBackgroundRole(QPalette::Dark); + const int verticalMargin = Utils::dpiScaled(3); + const int horizontalMargin = Utils::dpiScaled(6); + label->setContentsMargins(horizontalMargin, verticalMargin, + horizontalMargin, verticalMargin); + + label->setAutoFillBackground(true); + + m_gridLayout->addWidget(label, m_rowIndex, 0, 1, ColumnCount); + ++m_rowIndex; +} + +void VariantEditor::addSeparator() +{ + auto separator = new QFrame(m_widget); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Plain); + separator->setForegroundRole(QPalette::Dark); + m_gridLayout->addWidget(separator, m_rowIndex, 0, 1, ColumnCount); + ++m_rowIndex; +} + +void VariantEditor::addValue(const QString &name, const QVariant &value) +{ + auto label = new LineEditLabel(name, m_widget); + // label->setBuddy(m_widget)); // todo: associate with widget somehow + label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); + addValue(value); +} + +void VariantEditor::addValue(const QVariant &value) +{ + const int type = value.userType(); + switch (type) { + case QMetaType::QVariantList: { + const auto list = value.toList(); + for (const auto &item : list) + addValue(item); + break; + } + case QMetaType::QVariantMap: { + const auto map = value.toMap(); + for (auto it = map.constBegin(); it != map.constEnd(); ++it) + addValue(it.key(), it.value()); + break; + } + default: { + auto factory = m_factories.find(type); + if (factory != m_factories.end()) { + const auto editor = factory->second->createEditor(value, m_widget); + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); + } else { + qDebug() << "No editor factory for type" << type; + } + ++m_rowIndex; + } + } +} + +QSize VariantEditor::viewportSizeHint() const +{ + return m_widget->minimumSizeHint(); +} + +} // namespace Tiled + +#include "moc_varianteditor.cpp" +#include "varianteditor.moc" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h new file mode 100644 index 0000000000..2e46be7ead --- /dev/null +++ b/src/tiled/varianteditor.h @@ -0,0 +1,82 @@ +/* + * varianteditor.h + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +class QGridLayout; + +namespace Tiled { + +class EditorFactory +{ + Q_DECLARE_TR_FUNCTIONS(EditorFactory) + +public: + virtual QWidget *createEditor(const QVariant &value, + QWidget *parent) = 0; + + virtual QVariant value(QWidget *editor) const { return {}; } + virtual void setValue(QWidget *editor, const QVariant &value) {}; +}; + +class VariantEditor : public QScrollArea +{ + Q_OBJECT + +public: + VariantEditor(QWidget *parent = nullptr); + + void registerEditorFactory(int type, std::unique_ptr factory); + + void addHeader(const QString &text); + void addSeparator(); + void addValue(const QString &name, const QVariant &value); + void addValue(const QVariant &value); + +private: + enum Column { + LeftSpacing, + LabelColumn, + MiddleSpacing, + WidgetColumn, + RightSpacing, + ColumnCount, + }; + + QWidget *m_widget; + QGridLayout *m_gridLayout; + int m_rowIndex = 0; + std::unordered_map> m_factories; + + // QAbstractScrollArea interface +protected: + QSize viewportSizeHint() const override; +}; + +} // namespace Tiled From 5179a8e9ac966eb86a7faed2d916d5dcdebf50e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 21 Aug 2024 15:50:06 +0200 Subject: [PATCH 02/78] Introduced a responsive editor for QSize It adjusts the layout based on its width. Also made a few other visual tweaks. --- src/tiled/varianteditor.cpp | 153 +++++++++++++++++++++++++++--------- src/tiled/varianteditor.h | 24 +++++- 2 files changed, 135 insertions(+), 42 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index d2bc1f7327..7b1a1357f6 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -317,36 +317,107 @@ class PointFEditorFactory : public EditorFactory } }; -class SizeEditorFactory : public EditorFactory +class SizeEditor : public QWidget { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + SizeEditor(QWidget *parent = nullptr) + : QWidget(parent) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_widthSpinBox(new SpinBox(this)) + , m_heightSpinBox(new SpinBox(this)) { - auto editor = new QWidget(parent); - auto horizontalLayout = new QHBoxLayout(editor); - horizontalLayout->setContentsMargins(QMargins()); - - auto widthLabel = new QLabel(QStringLiteral("W"), editor); - widthLabel->setToolTip(tr("Width")); - horizontalLayout->addWidget(widthLabel, 0, Qt::AlignRight); + m_widthLabel->setAlignment(Qt::AlignCenter); + m_heightLabel->setAlignment(Qt::AlignCenter); + + auto layout = new QGridLayout(this); + layout->setContentsMargins(QMargins()); + layout->setColumnStretch(1, 1); + layout->setColumnStretch(3, 1); + layout->setSpacing(Utils::dpiScaled(3)); + + const int horizontalMargin = Utils::dpiScaled(3); + m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + } - auto widthSpinBox = new SpinBox(editor); - widthLabel->setBuddy(widthSpinBox); - horizontalLayout->addWidget(widthSpinBox, 1); + void setValue(const QSize &size) + { + m_widthSpinBox->setValue(size.width()); + m_heightSpinBox->setValue(size.height()); + } - auto heightLabel = new QLabel(QStringLiteral("H"), editor); - heightLabel->setToolTip(tr("Height")); - horizontalLayout->addWidget(heightLabel, 0, Qt::AlignRight); + QSize value() const + { + return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); + } - auto heightSpinBox = new SpinBox(editor); - heightLabel->setBuddy(heightSpinBox); - horizontalLayout->addWidget(heightSpinBox, 1); +private: + void resizeEvent(QResizeEvent *event) override + { + QWidget::resizeEvent(event); + + const auto direction = event->size().width() < minimumHorizontalWidth() + ? Qt::Vertical : Qt::Horizontal; + + if (m_direction != direction) { + m_direction = direction; + + auto layout = qobject_cast(this->layout()); + + // Remove all widgets from layout, without deleting them + layout->removeWidget(m_widthLabel); + layout->removeWidget(m_widthSpinBox); + layout->removeWidget(m_heightLabel); + layout->removeWidget(m_heightSpinBox); + + if (direction == Qt::Horizontal) { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + layout->setColumnStretch(3, 1); + } else { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 1, 0); + layout->addWidget(m_heightSpinBox, 1, 1); + layout->setColumnStretch(3, 0); + } + + // this avoids flickering when the layout changes + layout->activate(); + } + } - // horizontalLayout->addStretch(); + int minimumHorizontalWidth() const + { + return m_widthLabel->minimumSizeHint().width() + + m_widthSpinBox->minimumSizeHint().width() + + m_heightLabel->minimumSizeHint().width() + + m_heightSpinBox->minimumSizeHint().width() + + layout()->spacing() * 3; + } - widthSpinBox->setValue(value.toSize().width()); - heightSpinBox->setValue(value.toSize().height()); + Qt::Orientation m_direction = Qt::Horizontal; + QLabel *m_widthLabel; + QLabel *m_heightLabel; + SpinBox *m_widthSpinBox; + SpinBox *m_heightSpinBox; +}; +class SizeEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(const QVariant &value, QWidget *parent) override + { + auto editor = new SizeEditor(parent); + editor->setValue(value.toSize()); return editor; } }; @@ -450,16 +521,17 @@ VariantEditor::VariantEditor(QWidget *parent) : QScrollArea(parent) { m_widget = new QWidget; + m_widget->setBackgroundRole(QPalette::Base); auto verticalLayout = new QVBoxLayout(m_widget); m_gridLayout = new QGridLayout; verticalLayout->addLayout(m_gridLayout); verticalLayout->addStretch(); - verticalLayout->setContentsMargins(0, 0, 0, 0); + verticalLayout->setContentsMargins(QMargins()); setWidget(m_widget); setWidgetResizable(true); - m_gridLayout->setContentsMargins(0, 0, 0, 0); + m_gridLayout->setContentsMargins(QMargins()); m_gridLayout->setSpacing(Utils::dpiScaled(3)); m_gridLayout->setColumnStretch(LabelColumn, 2); @@ -605,9 +677,6 @@ void VariantEditor::registerEditorFactory(int type, std::unique_ptrfont(); - boldFont.setBold(true); - label->setFont(boldFont); label->setBackgroundRole(QPalette::Dark); const int verticalMargin = Utils::dpiScaled(3); const int horizontalMargin = Utils::dpiScaled(6); @@ -625,7 +694,7 @@ void VariantEditor::addSeparator() auto separator = new QFrame(m_widget); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); - separator->setForegroundRole(QPalette::Dark); + separator->setForegroundRole(QPalette::Mid); m_gridLayout->addWidget(separator, m_rowIndex, 0, 1, ColumnCount); ++m_rowIndex; } @@ -633,10 +702,12 @@ void VariantEditor::addSeparator() void VariantEditor::addValue(const QString &name, const QVariant &value) { auto label = new LineEditLabel(name, m_widget); - // label->setBuddy(m_widget)); // todo: associate with widget somehow label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); - addValue(value); + if (auto editor = createEditor(value)) { + m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); + } + ++m_rowIndex; } void VariantEditor::addValue(const QVariant &value) @@ -656,22 +727,28 @@ void VariantEditor::addValue(const QVariant &value) break; } default: { - auto factory = m_factories.find(type); - if (factory != m_factories.end()) { - const auto editor = factory->second->createEditor(value, m_widget); - editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); - } else { + if (auto editor = createEditor(value)) + m_gridLayout->addWidget(editor, m_rowIndex, LabelColumn, 1, 3); + else qDebug() << "No editor factory for type" << type; - } + ++m_rowIndex; } } } -QSize VariantEditor::viewportSizeHint() const +QWidget *VariantEditor::createEditor(const QVariant &value) { - return m_widget->minimumSizeHint(); + const int type = value.userType(); + auto factory = m_factories.find(type); + if (factory != m_factories.end()) { + const auto editor = factory->second->createEditor(value, m_widget); + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + return editor; + } else { + qDebug() << "No editor factory for type" << type; + } + return nullptr; } } // namespace Tiled diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 2e46be7ead..22cf2023e7 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -33,6 +33,25 @@ class QGridLayout; namespace Tiled { +class Property : public QObject +{ + Q_OBJECT + +public: + Property(QObject *parent = nullptr) + : QObject(parent) + { + } + + virtual QWidget *createEditor(QWidget *parent) = 0; + + virtual QVariant value() const = 0; + virtual void setValue(const QVariant &value) = 0; + +signals: + void valueChanged(const QVariant &value); +}; + class EditorFactory { Q_DECLARE_TR_FUNCTIONS(EditorFactory) @@ -58,6 +77,7 @@ class VariantEditor : public QScrollArea void addSeparator(); void addValue(const QString &name, const QVariant &value); void addValue(const QVariant &value); + QWidget *createEditor(const QVariant &value); private: enum Column { @@ -73,10 +93,6 @@ class VariantEditor : public QScrollArea QGridLayout *m_gridLayout; int m_rowIndex = 0; std::unordered_map> m_factories; - - // QAbstractScrollArea interface -protected: - QSize viewportSizeHint() const override; }; } // namespace Tiled From 1637962c53d6c0815ad660825e9a908eaccdda01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 26 Aug 2024 10:46:08 +0200 Subject: [PATCH 03/78] First fully functional "Map Orientation" property Introduced Property class and changed from using editor factories to having the property itself create its widget. --- src/tiled/propertiesdock.cpp | 12 +-- src/tiled/propertiesdock.h | 4 +- src/tiled/propertieswidget.cpp | 97 ++++++++++++++++++++-- src/tiled/propertieswidget.h | 4 +- src/tiled/varianteditor.cpp | 147 ++++++++++++++++++++++----------- src/tiled/varianteditor.h | 85 ++++++++++++++++--- 6 files changed, 271 insertions(+), 78 deletions(-) diff --git a/src/tiled/propertiesdock.cpp b/src/tiled/propertiesdock.cpp index f47e2e10f0..36cde21f64 100644 --- a/src/tiled/propertiesdock.cpp +++ b/src/tiled/propertiesdock.cpp @@ -20,7 +20,7 @@ #include "propertiesdock.h" -#include "varianteditor.h" +#include "propertieswidget.h" #include @@ -28,26 +28,26 @@ namespace Tiled { PropertiesDock::PropertiesDock(QWidget *parent) : QDockWidget(parent) - , mPropertiesWidget(new VariantEditor(this)) + , mPropertiesWidget(new PropertiesWidget(this)) { setObjectName(QLatin1String("propertiesDock")); setWidget(mPropertiesWidget); - // connect(mPropertiesWidget, &PropertiesWidget::bringToFront, - // this, &PropertiesDock::bringToFront); + connect(mPropertiesWidget, &PropertiesWidget::bringToFront, + this, &PropertiesDock::bringToFront); retranslateUi(); } void PropertiesDock::setDocument(Document *document) { - // mPropertiesWidget->setDocument(document); + mPropertiesWidget->setDocument(document); } void PropertiesDock::selectCustomProperty(const QString &name) { bringToFront(); - // mPropertiesWidget->selectCustomProperty(name); + mPropertiesWidget->selectCustomProperty(name); } bool PropertiesDock::event(QEvent *event) diff --git a/src/tiled/propertiesdock.h b/src/tiled/propertiesdock.h index e5e96dbdd7..c39532484d 100644 --- a/src/tiled/propertiesdock.h +++ b/src/tiled/propertiesdock.h @@ -26,7 +26,7 @@ namespace Tiled { class Document; -class VariantEditor; +class PropertiesWidget; class PropertiesDock : public QDockWidget { @@ -50,7 +50,7 @@ public slots: void bringToFront(); void retranslateUi(); - VariantEditor *mPropertiesWidget; + PropertiesWidget *mPropertiesWidget; }; } // namespace Tiled diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index e118088c2f..f8ac584c89 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -22,13 +22,13 @@ #include "actionmanager.h" #include "addpropertydialog.h" +#include "changemapproperty.h" #include "changeproperties.h" #include "clipboardmanager.h" #include "mapdocument.h" -#include "mapobject.h" #include "propertybrowser.h" #include "utils.h" -#include "variantpropertymanager.h" +#include "varianteditor.h" #include #include @@ -46,7 +46,7 @@ namespace Tiled { PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mDocument(nullptr) - , mPropertyBrowser(new PropertyBrowser) + , mPropertyBrowser(new VariantEditor(this)) { mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); @@ -89,8 +89,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, this, &PropertiesWidget::showContextMenu); - connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, - this, &PropertiesWidget::updateActions); + // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, + // this, &PropertiesWidget::updateActions); retranslateUi(); } @@ -110,7 +110,7 @@ void PropertiesWidget::setDocument(Document *document) mDocument->disconnect(this); mDocument = document; - mPropertyBrowser->setDocument(document); + // mPropertyBrowser->setDocument(document); if (document) { connect(document, &Document::currentObjectChanged, @@ -131,7 +131,7 @@ void PropertiesWidget::setDocument(Document *document) void PropertiesWidget::selectCustomProperty(const QString &name) { - mPropertyBrowser->selectCustomProperty(name); + // mPropertyBrowser->selectCustomProperty(name); } static bool anyObjectHasProperty(const QList &objects, const QString &name) @@ -143,9 +143,76 @@ static bool anyObjectHasProperty(const QList &objects, const QString &n return false; } +class MapOrientationProperty : public EnumProperty +{ +public: + MapOrientationProperty(MapDocument *mapDocument) + : EnumProperty(tr("Orientation")) + , mMapDocument(mapDocument) + { + setEnumNames({ + tr("Orthogonal"), + tr("Isometric"), + tr("Isometric (Staggered)"), + tr("Hexagonal (Staggered)"), + }); + setEnumValues({ + Map::Orthogonal, + Map::Isometric, + Map::Staggered, + Map::Hexagonal, + }); + + connect(mMapDocument, &MapDocument::mapChanged, + this, &Property::valueChanged); + } + + QVariant value() const override + { + return mMapDocument->map()->orientation(); + } + + void setValue(const QVariant &value) override + { + Map::Orientation orientation = static_cast(value.toInt()); + auto command = new ChangeMapProperty(mMapDocument, orientation); + mMapDocument->undoStack()->push(command); + } + +private: + MapDocument *mMapDocument; +}; + void PropertiesWidget::currentObjectChanged(Object *object) { - mPropertyBrowser->setObject(object); + // mPropertyBrowser->setObject(object); + mPropertyBrowser->clear(); + + if (object) { + switch (object->typeId()) { + case Object::LayerType: + case Object::MapObjectType: + break; + case Object::MapType: { + Map *map = static_cast(object); + mPropertyBrowser->addHeader(tr("Map")); + mPropertyBrowser->addProperty(new MapOrientationProperty(static_cast(mDocument))); + + auto sizeProperty = new VariantProperty(tr("Map Size"), map->size()); + sizeProperty->setEnabled(false); + mPropertyBrowser->addProperty(sizeProperty); + + break; + } + case Object::TilesetType: + case Object::TileType: + case Object::WangSetType: + case Object::WangColorType: + case Object::ProjectType: + case Object::WorldType: + break; + } + } bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; bool isTileset = object && object->isPartOfTileset(); @@ -157,6 +224,7 @@ void PropertiesWidget::currentObjectChanged(Object *object) void PropertiesWidget::updateActions() { +#if 0 const QList items = mPropertyBrowser->selectedItems(); bool allCustomProperties = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; @@ -176,6 +244,7 @@ void PropertiesWidget::updateActions() mActionRemoveProperty->setEnabled(canModify); mActionRenameProperty->setEnabled(canModify && items.size() == 1); +#endif } void PropertiesWidget::cutProperties() @@ -186,6 +255,7 @@ void PropertiesWidget::cutProperties() bool PropertiesWidget::copyProperties() { +#if 0 Object *object = mPropertyBrowser->object(); if (!object) return false; @@ -206,6 +276,7 @@ bool PropertiesWidget::copyProperties() } ClipboardManager::instance()->setProperties(properties); +#endif return true; } @@ -269,11 +340,12 @@ void PropertiesWidget::addProperty(const QString &name, const QVariant &value) name, value)); } - mPropertyBrowser->editCustomProperty(name); + // mPropertyBrowser->editCustomProperty(name); } void PropertiesWidget::removeProperties() { +#if 0 Object *object = mDocument->currentObject(); if (!object) return; @@ -299,10 +371,12 @@ void PropertiesWidget::removeProperties() } undoStack->endMacro(); +#endif } void PropertiesWidget::renameProperty() { +#if 0 QtBrowserItem *item = mPropertyBrowser->currentItem(); if (!mPropertyBrowser->isCustomPropertyItem(item)) return; @@ -317,10 +391,12 @@ void PropertiesWidget::renameProperty() dialog->setWindowTitle(QCoreApplication::translate("Tiled::PropertiesDock", "Rename Property")); connect(dialog, &QInputDialog::textValueSelected, this, &PropertiesWidget::renamePropertyTo); dialog->open(); +#endif } void PropertiesWidget::renamePropertyTo(const QString &name) { +#if 0 if (name.isEmpty()) return; @@ -334,10 +410,12 @@ void PropertiesWidget::renamePropertyTo(const QString &name) QUndoStack *undoStack = mDocument->undoStack(); undoStack->push(new RenameProperty(mDocument, mDocument->currentObjects(), oldName, name)); +#endif } void PropertiesWidget::showContextMenu(const QPoint &pos) { +#if 0 const Object *object = mDocument->currentObject(); if (!object) return; @@ -474,6 +552,7 @@ void PropertiesWidget::showContextMenu(const QPoint &pos) undoStack->endMacro(); } +#endif } bool PropertiesWidget::event(QEvent *event) diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 022a520b2f..dc1e2cefe0 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -27,7 +27,7 @@ namespace Tiled { class Object; class Document; -class PropertyBrowser; +class VariantEditor; /** * The PropertiesWidget combines the PropertyBrowser with some controls that @@ -74,7 +74,7 @@ public slots: void retranslateUi(); Document *mDocument; - PropertyBrowser *mPropertyBrowser; + VariantEditor *mPropertyBrowser; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 7b1a1357f6..6a644d3715 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -206,11 +207,6 @@ class StringEditorFactory : public EditorFactory editor->setText(value.toString()); return editor; } - - QVariant value(QWidget *editor) const override - { - return static_cast(editor)->text(); - } }; class IntEditorFactory : public EditorFactory @@ -276,8 +272,6 @@ class PointEditorFactory : public EditorFactory yLabel->setBuddy(ySpinBox); horizontalLayout->addWidget(ySpinBox, 1); - // horizontalLayout->addStretch(); - xSpinBox->setValue(value.toPoint().x()); ySpinBox->setValue(value.toPoint().y()); @@ -308,8 +302,6 @@ class PointFEditorFactory : public EditorFactory yLabel->setBuddy(ySpinBox); horizontalLayout->addWidget(ySpinBox, 1); - // horizontalLayout->addStretch(); - xSpinBox->setValue(value.toPointF().x()); ySpinBox->setValue(value.toPointF().y()); @@ -488,17 +480,7 @@ class EnumEditorFactory : public EditorFactory public: QWidget *createEditor(const QVariant &value, QWidget *parent) override { - auto editor = new QComboBox(parent); - // This allows the combo box to shrink horizontally. - editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); - editor->setModel(&m_enumNamesModel); - - if (m_enumValues.isEmpty()) - editor->setCurrentIndex(value.toInt()); - else - editor->setCurrentIndex(m_enumValues.indexOf(value.toInt())); - - return editor; + return nullptr; } void setEnumNames(const QStringList &enumNames) @@ -516,6 +498,51 @@ class EnumEditorFactory : public EditorFactory QList m_enumValues; }; +void VariantProperty::setValue(const QVariant &value) +{ + if (m_value != value) { + m_value = value; + emit valueChanged(); + } +} + +QWidget *VariantProperty::createEditor(QWidget *parent) +{ + switch (m_value.userType()) { + case QMetaType::QSize: + return SizeEditorFactory().createEditor(m_value, parent); + default: + break; + } + + return nullptr; +} + +QWidget *EnumProperty::createEditor(QWidget *parent) +{ + auto editor = new QComboBox(parent); + // This allows the combo box to shrink horizontally. + editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + editor->setModel(&m_enumNamesModel); + + auto syncEditor = [editor, this]() { + const QSignalBlocker blocker(editor); + if (m_enumValues.isEmpty()) + editor->setCurrentIndex(value().toInt()); + else + editor->setCurrentIndex(m_enumValues.indexOf(value().toInt())); + }; + syncEditor(); + + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, qOverload(&QComboBox::currentIndexChanged), this, + [this](int index) { + setValue(m_enumValues.isEmpty() ? index : m_enumValues.at(index)); + }); + + return editor; +} + VariantEditor::VariantEditor(QWidget *parent) : QScrollArea(parent) @@ -647,26 +674,28 @@ VariantEditor::VariantEditor(QWidget *parent) // QVariant(3.14) // }); - addHeader(tr("Map")); - addValue(tr("Class"), QString()); - addSeparator(); - addValue(tr("Orientation"), QVariant::fromValue(Map::Hexagonal)); - addValue(tr("Infinite"), false); - addValue(tr("Map Size"), QSize(20, 20)); - addValue(tr("Tile Size"), QSize(14, 12)); - addValue(tr("Tile Side Length (Hex)"), 6); - addValue(tr("Stagger Axis"), QVariant::fromValue(Map::StaggerY)); - addValue(tr("Stagger Index"), QVariant::fromValue(Map::StaggerEven)); - addSeparator(); - addValue(tr("Parallax Origin"), QPointF()); - addSeparator(); - addValue(tr("Tile Layer Format"), QVariant::fromValue(Map::Base64Zlib)); - addValue(tr("Output Chunk Size"), QSize(16, 16)); - addValue(tr("Compression Level"), -1); - addSeparator(); - addValue(tr("Tile Render Order"), QVariant::fromValue(Map::RightDown)); - addValue(tr("Background Color"), QColor()); - addHeader(tr("Custom Properties")); + // addHeader(tr("Map")); + // addProperty(new VariantProperty(tr("Class"), QString())); + // addProperty(new VariantProperty(tr("Orientation"), QVariant::fromValue(Map::Hexagonal))); + // addValue(tr("Class"), QString()); + // addSeparator(); + // addValue(tr("Orientation"), QVariant::fromValue(Map::Hexagonal)); + // addValue(tr("Infinite"), false); + // addValue(tr("Map Size"), QSize(20, 20)); + // addValue(tr("Tile Size"), QSize(14, 12)); + // addValue(tr("Tile Side Length (Hex)"), 6); + // addValue(tr("Stagger Axis"), QVariant::fromValue(Map::StaggerY)); + // addValue(tr("Stagger Index"), QVariant::fromValue(Map::StaggerEven)); + // addSeparator(); + // addValue(tr("Parallax Origin"), QPointF()); + // addSeparator(); + // addValue(tr("Tile Layer Format"), QVariant::fromValue(Map::Base64Zlib)); + // addValue(tr("Output Chunk Size"), QSize(16, 16)); + // addValue(tr("Compression Level"), -1); + // addSeparator(); + // addValue(tr("Tile Render Order"), QVariant::fromValue(Map::RightDown)); + // addValue(tr("Background Color"), QColor()); + // addHeader(tr("Custom Properties")); } void VariantEditor::registerEditorFactory(int type, std::unique_ptr factory) @@ -674,6 +703,16 @@ void VariantEditor::registerEditorFactory(int type, std::unique_ptrtakeAt(0))) { + delete item->widget(); + delete item; + } + m_rowIndex = 0; +} + void VariantEditor::addHeader(const QString &text) { auto label = new ElidingLabel(text, m_widget); @@ -699,17 +738,23 @@ void VariantEditor::addSeparator() ++m_rowIndex; } -void VariantEditor::addValue(const QString &name, const QVariant &value) +void VariantEditor::addProperty(Property *property) { - auto label = new LineEditLabel(name, m_widget); + auto label = new LineEditLabel(property->name(), m_widget); label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + label->setEnabled(property->isEnabled()); + connect(property, &Property::enabledChanged, label, &QLabel::setEnabled); m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); - if (auto editor = createEditor(value)) { + + if (auto editor = createEditor(property)) { + editor->setEnabled(property->isEnabled()); + connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); } ++m_rowIndex; } +#if 0 void VariantEditor::addValue(const QVariant &value) { const int type = value.userType(); @@ -736,17 +781,21 @@ void VariantEditor::addValue(const QVariant &value) } } } +#endif -QWidget *VariantEditor::createEditor(const QVariant &value) +QWidget *VariantEditor::createEditor(Property *property) { - const int type = value.userType(); - auto factory = m_factories.find(type); - if (factory != m_factories.end()) { - const auto editor = factory->second->createEditor(value, m_widget); + const auto editor = property->createEditor(m_widget); + // const auto value = property->value(); + // const int type = value.userType(); + // auto factory = m_factories.find(type); + // if (factory != m_factories.end()) { + // const auto editor = factory->second->createEditor(value, m_widget); + if (editor) { editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); return editor; } else { - qDebug() << "No editor factory for type" << type; + qDebug() << "No editor for property" << property->name(); } return nullptr; } diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 22cf2023e7..8fe48c725a 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -36,20 +37,85 @@ namespace Tiled { class Property : public QObject { Q_OBJECT + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) public: - Property(QObject *parent = nullptr) + Property(const QString &name, QObject *parent = nullptr) : QObject(parent) + , m_name(name) + {} + + QString name() const { return m_name; } + + bool isEnabled() const { return m_enabled; } + void setEnabled(bool enabled) { + if (m_enabled != enabled) { + m_enabled = enabled; + emit enabledChanged(enabled); + } } - virtual QWidget *createEditor(QWidget *parent) = 0; - virtual QVariant value() const = 0; virtual void setValue(const QVariant &value) = 0; + virtual QWidget *createEditor(QWidget *parent) = 0; + signals: - void valueChanged(const QVariant &value); + void valueChanged(); + void enabledChanged(bool enabled); + +private: + QString m_name; + bool m_enabled = true; +}; + +class VariantProperty : public Property +{ + Q_OBJECT + +public: + VariantProperty(const QString &name, const QVariant &value, QObject *parent = nullptr) + : Property(name, parent) + , m_value(value) + { + } + + QVariant value() const override { return m_value; } + void setValue(const QVariant &value) override; + + QWidget *createEditor(QWidget *parent) override; + +private: + QVariant m_value; +}; + +class EnumProperty : public Property +{ + Q_OBJECT + +public: + EnumProperty(const QString &name, QObject *parent = nullptr) + : Property(name, parent) + {} + + QWidget *createEditor(QWidget *parent) override; + + void setEnumNames(const QStringList &enumNames) + { + m_enumNamesModel.setStringList(enumNames); + } + + void setEnumValues(const QList &enumValues) + { + m_enumValues = enumValues; + } + +private: + QStringListModel m_enumNamesModel; + QList m_enumValues; }; class EditorFactory @@ -59,9 +125,6 @@ class EditorFactory public: virtual QWidget *createEditor(const QVariant &value, QWidget *parent) = 0; - - virtual QVariant value(QWidget *editor) const { return {}; } - virtual void setValue(QWidget *editor, const QVariant &value) {}; }; class VariantEditor : public QScrollArea @@ -73,13 +136,15 @@ class VariantEditor : public QScrollArea void registerEditorFactory(int type, std::unique_ptr factory); + void clear(); void addHeader(const QString &text); void addSeparator(); - void addValue(const QString &name, const QVariant &value); - void addValue(const QVariant &value); - QWidget *createEditor(const QVariant &value); + void addProperty(Property *property); + // void addValue(const QVariant &value); private: + QWidget *createEditor(Property *property); + enum Column { LeftSpacing, LabelColumn, From 8113c2b068d3cafb4515011fb7d4d7d988f5dc09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 30 Aug 2024 17:14:30 +0200 Subject: [PATCH 04/78] Implemented "Map Size" and "Tile Size" properties * Map size is read-only but features a "Resize Map" button that triggers the resize dialog. * Both map size and tile size are implemented based on the new "ValueTypeEditorFactory" which selects an editor based on the value type. This functionality is moved out of the VariantEditor. * Introduced AbstractProperty, ValueProperty and QObjectProperty in an attempt to provide some convenience on top of the Property interface. I'm not entirely convinced it was a good idea to put the "createEditor" function on the Property. It makes it easy to do custom widgets for a property, but annoying to have it use a type-based editor. --- src/tiled/propertieswidget.cpp | 117 ++++++- src/tiled/propertieswidget.h | 4 + src/tiled/varianteditor.cpp | 561 ++++++++++++++++++++------------- src/tiled/varianteditor.h | 170 ++++++++-- 4 files changed, 593 insertions(+), 259 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index f8ac584c89..0e7a882c01 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mDocument(nullptr) , mPropertyBrowser(new VariantEditor(this)) + , mDefaultEditorFactory(std::make_unique()) { mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); @@ -145,6 +147,8 @@ static bool anyObjectHasProperty(const QList &objects, const QString &n class MapOrientationProperty : public EnumProperty { + Q_OBJECT + public: MapOrientationProperty(MapDocument *mapDocument) : EnumProperty(tr("Orientation")) @@ -183,6 +187,98 @@ class MapOrientationProperty : public EnumProperty MapDocument *mMapDocument; }; +class MapSizeProperty : public AbstractProperty +{ + Q_OBJECT + +public: + MapSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory) + : AbstractProperty(tr("Map Size"), editorFactory) + , mMapDocument(mapDocument) + { + connect(mMapDocument, &MapDocument::mapChanged, + this, &Property::valueChanged); + } + + QVariant value() const override { return mMapDocument->map()->size(); } + void setValue(const QVariant &) override {}; + + QWidget *createEditor(QWidget *parent) override + { + auto widget = new QWidget(parent); + auto layout = new QVBoxLayout(widget); + auto valueEdit = AbstractProperty::createEditor(widget); + auto resizeButton = new QPushButton(tr("Resize Map"), widget); + + valueEdit->setEnabled(false); + layout->setContentsMargins(QMargins()); + layout->addWidget(valueEdit); + layout->addWidget(resizeButton, 0, Qt::AlignLeft); + + connect(resizeButton, &QPushButton::clicked, [] { + ActionManager::action("ResizeMap")->trigger(); + }); + + return widget; + } + +private: + MapDocument *mMapDocument; +}; + +class TileSizeProperty : public AbstractProperty +{ + Q_OBJECT + +public: + TileSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory) + : AbstractProperty(tr("Tile Size"), editorFactory) + , mMapDocument(mapDocument) + { + connect(mMapDocument, &Document::changed, + this, &TileSizeProperty::onChanged); + } + + QVariant value() const override + { + return mMapDocument->map()->tileSize(); + } + + void setValue(const QVariant &value) override + { + auto oldSize = mMapDocument->map()->tileSize(); + auto newSize = value.toSize(); + + if (oldSize.width() != newSize.width()) { + auto command = new ChangeMapProperty(mMapDocument, + Map::TileWidthProperty, + newSize.width()); + mMapDocument->undoStack()->push(command); + } + + if (oldSize.height() != newSize.height()) { + auto command = new ChangeMapProperty(mMapDocument, + Map::TileHeightProperty, + newSize.height()); + mMapDocument->undoStack()->push(command); + } + }; + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::MapChanged) + return; + + const auto property = static_cast(event).property; + if (property == Map::TileWidthProperty || property == Map::TileHeightProperty) + emit valueChanged(); + } + + MapDocument *mMapDocument; +}; + + void PropertiesWidget::currentObjectChanged(Object *object) { // mPropertyBrowser->setObject(object); @@ -195,12 +291,22 @@ void PropertiesWidget::currentObjectChanged(Object *object) break; case Object::MapType: { Map *map = static_cast(object); - mPropertyBrowser->addHeader(tr("Map")); - mPropertyBrowser->addProperty(new MapOrientationProperty(static_cast(mDocument))); + auto mapDocument = static_cast(mDocument); - auto sizeProperty = new VariantProperty(tr("Map Size"), map->size()); - sizeProperty->setEnabled(false); - mPropertyBrowser->addProperty(sizeProperty); + mPropertyBrowser->addHeader(tr("Map")); + mPropertyBrowser->addProperty(new MapOrientationProperty(mapDocument)); + mPropertyBrowser->addProperty(new MapSizeProperty(mapDocument, mDefaultEditorFactory.get())); + mPropertyBrowser->addProperty(new TileSizeProperty(mapDocument, mDefaultEditorFactory.get())); + // todo: infinite + // todo: hex side length + // todo: stagger axis + // todo: stagger index + // todo: parallax origin + // todo: layer data format + // todo: chunk size + // todo: tile render order + // todo: compression level + // todo: background color break; } @@ -608,3 +714,4 @@ void PropertiesWidget::retranslateUi() } // namespace Tiled #include "moc_propertieswidget.cpp" +#include "propertieswidget.moc" diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index dc1e2cefe0..f9f5db095e 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -22,11 +22,14 @@ #include +#include + namespace Tiled { class Object; class Document; +class ValueTypeEditorFactory; class VariantEditor; /** @@ -75,6 +78,7 @@ public slots: Document *mDocument; VariantEditor *mPropertyBrowser; + std::unique_ptr mDefaultEditorFactory; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 6a644d3715..60d2290417 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -22,23 +22,35 @@ #include "colorbutton.h" #include "compression.h" #include "map.h" -#include "tiled.h" #include "utils.h" #include #include #include -#include #include #include #include #include +#include #include #include #include namespace Tiled { +AbstractProperty::AbstractProperty(const QString &name, + EditorFactory *editorFactory, + QObject *parent) + : Property(name, parent) + , m_editorFactory(editorFactory) +{} + +QWidget *AbstractProperty::createEditor(QWidget *parent) +{ + return m_editorFactory->createEditor(this, parent); +} + + class SpinBox : public QSpinBox { Q_OBJECT @@ -201,8 +213,9 @@ class LineEditLabel : public ElidingLabel class StringEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new QLineEdit(parent); editor->setText(value.toString()); return editor; @@ -212,8 +225,9 @@ class StringEditorFactory : public EditorFactory class IntEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new SpinBox(parent); editor->setValue(value.toInt()); return editor; @@ -223,8 +237,9 @@ class IntEditorFactory : public EditorFactory class FloatEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new DoubleSpinBox(parent); editor->setValue(value.toDouble()); return editor; @@ -234,8 +249,9 @@ class FloatEditorFactory : public EditorFactory class BoolEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new QCheckBox(parent); bool checked = value.toBool(); editor->setChecked(checked); @@ -252,8 +268,9 @@ class BoolEditorFactory : public EditorFactory class PointEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new QWidget(parent); auto horizontalLayout = new QHBoxLayout(editor); horizontalLayout->setContentsMargins(QMargins()); @@ -282,8 +299,9 @@ class PointEditorFactory : public EditorFactory class PointFEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new QWidget(parent); auto horizontalLayout = new QHBoxLayout(editor); horizontalLayout->setContentsMargins(QMargins()); @@ -309,94 +327,29 @@ class PointFEditorFactory : public EditorFactory } }; -class SizeEditor : public QWidget +/** + * A widget for editing a QSize value. + */ +class SizeEdit : public QWidget { + Q_OBJECT + Q_PROPERTY(QSize value READ value WRITE setValue NOTIFY valueChanged FINAL) + public: - SizeEditor(QWidget *parent = nullptr) - : QWidget(parent) - , m_widthLabel(new QLabel(QStringLiteral("W"), this)) - , m_heightLabel(new QLabel(QStringLiteral("H"), this)) - , m_widthSpinBox(new SpinBox(this)) - , m_heightSpinBox(new SpinBox(this)) - { - m_widthLabel->setAlignment(Qt::AlignCenter); - m_heightLabel->setAlignment(Qt::AlignCenter); - - auto layout = new QGridLayout(this); - layout->setContentsMargins(QMargins()); - layout->setColumnStretch(1, 1); - layout->setColumnStretch(3, 1); - layout->setSpacing(Utils::dpiScaled(3)); - - const int horizontalMargin = Utils::dpiScaled(3); - m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); - } + SizeEdit(QWidget *parent = nullptr); - void setValue(const QSize &size) - { - m_widthSpinBox->setValue(size.width()); - m_heightSpinBox->setValue(size.height()); - } + void setValue(const QSize &size); + QSize value() const; - QSize value() const - { - return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); - } +signals: + void valueChanged(); private: - void resizeEvent(QResizeEvent *event) override - { - QWidget::resizeEvent(event); - - const auto direction = event->size().width() < minimumHorizontalWidth() - ? Qt::Vertical : Qt::Horizontal; - - if (m_direction != direction) { - m_direction = direction; - - auto layout = qobject_cast(this->layout()); - - // Remove all widgets from layout, without deleting them - layout->removeWidget(m_widthLabel); - layout->removeWidget(m_widthSpinBox); - layout->removeWidget(m_heightLabel); - layout->removeWidget(m_heightSpinBox); - - if (direction == Qt::Horizontal) { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); - layout->setColumnStretch(3, 1); - } else { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 1, 0); - layout->addWidget(m_heightSpinBox, 1, 1); - layout->setColumnStretch(3, 0); - } - - // this avoids flickering when the layout changes - layout->activate(); - } - } + void resizeEvent(QResizeEvent *event) override; - int minimumHorizontalWidth() const - { - return m_widthLabel->minimumSizeHint().width() + - m_widthSpinBox->minimumSizeHint().width() + - m_heightLabel->minimumSizeHint().width() + - m_heightSpinBox->minimumSizeHint().width() + - layout()->spacing() * 3; - } + int minimumHorizontalWidth() const; - Qt::Orientation m_direction = Qt::Horizontal; + Qt::Orientation m_orientation = Qt::Horizontal; QLabel *m_widthLabel; QLabel *m_heightLabel; SpinBox *m_widthSpinBox; @@ -406,10 +359,21 @@ class SizeEditor : public QWidget class SizeEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { - auto editor = new SizeEditor(parent); - editor->setValue(value.toSize()); + auto editor = new SizeEdit(parent); + auto syncEditor = [property, editor]() { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toSize()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &SizeEdit::valueChanged, property, + [property, editor]() { + property->setValue(editor->value()); + }); + return editor; } }; @@ -417,8 +381,9 @@ class SizeEditorFactory : public EditorFactory class RectFEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new QWidget(parent); auto gridLayout = new QGridLayout(editor); gridLayout->setContentsMargins(QMargins()); @@ -467,38 +432,25 @@ class RectFEditorFactory : public EditorFactory class ColorEditorFactory : public EditorFactory { public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override + QWidget *createEditor(Property *property, QWidget *parent) override { + auto value = property->value(); auto editor = new ColorButton(parent); editor->setColor(value.value()); return editor; } }; -class EnumEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(const QVariant &value, QWidget *parent) override - { - return nullptr; - } - - void setEnumNames(const QStringList &enumNames) - { - m_enumNamesModel.setStringList(enumNames); - } - - void setEnumValues(const QList &enumValues) - { - m_enumValues = enumValues; - } -private: - QStringListModel m_enumNamesModel; - QList m_enumValues; -}; +ValueProperty::ValueProperty(const QString &name, + const QVariant &value, + EditorFactory *editorFactory, + QObject *parent) + : AbstractProperty(name, editorFactory, parent) + , m_value(value) +{} -void VariantProperty::setValue(const QVariant &value) +void ValueProperty::setValue(const QVariant &value) { if (m_value != value) { m_value = value; @@ -506,41 +458,23 @@ void VariantProperty::setValue(const QVariant &value) } } -QWidget *VariantProperty::createEditor(QWidget *parent) -{ - switch (m_value.userType()) { - case QMetaType::QSize: - return SizeEditorFactory().createEditor(m_value, parent); - default: - break; - } - return nullptr; -} +EnumProperty::EnumProperty(const QString &name, + const QStringList &enumNames, + const QList &enumValues, + QObject *parent) + : AbstractProperty(name, &m_editorFactory, parent) + , m_editorFactory(enumNames, enumValues) +{} -QWidget *EnumProperty::createEditor(QWidget *parent) +void EnumProperty::setEnumNames(const QStringList &enumNames) { - auto editor = new QComboBox(parent); - // This allows the combo box to shrink horizontally. - editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); - editor->setModel(&m_enumNamesModel); - - auto syncEditor = [editor, this]() { - const QSignalBlocker blocker(editor); - if (m_enumValues.isEmpty()) - editor->setCurrentIndex(value().toInt()); - else - editor->setCurrentIndex(m_enumValues.indexOf(value().toInt())); - }; - syncEditor(); - - connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, qOverload(&QComboBox::currentIndexChanged), this, - [this](int index) { - setValue(m_enumValues.isEmpty() ? index : m_enumValues.at(index)); - }); + m_editorFactory.setEnumNames(enumNames); +} - return editor; +void EnumProperty::setEnumValues(const QList &enumValues) +{ + m_editorFactory.setEnumValues(enumValues); } @@ -567,59 +501,35 @@ VariantEditor::VariantEditor(QWidget *parent) m_gridLayout->setColumnMinimumWidth(MiddleSpacing, Utils::dpiScaled(2)); m_gridLayout->setColumnMinimumWidth(RightSpacing, Utils::dpiScaled(3)); - registerEditorFactory(QMetaType::QString, std::make_unique()); - registerEditorFactory(QMetaType::Int, std::make_unique()); - registerEditorFactory(QMetaType::Double, std::make_unique()); - registerEditorFactory(QMetaType::Bool, std::make_unique()); - registerEditorFactory(QMetaType::QPoint, std::make_unique()); - registerEditorFactory(QMetaType::QPointF, std::make_unique()); - registerEditorFactory(QMetaType::QSize, std::make_unique()); - registerEditorFactory(QMetaType::QRectF, std::make_unique()); - registerEditorFactory(QMetaType::QColor, std::make_unique()); - - auto alignmentEditorFactory = std::make_unique(); - alignmentEditorFactory->setEnumNames({ - tr("Unspecified"), - tr("Top Left"), - tr("Top"), - tr("Top Right"), - tr("Left"), - tr("Center"), - tr("Right"), - tr("Bottom Left"), - tr("Bottom"), - tr("Bottom Right"), - }); - registerEditorFactory(qMetaTypeId(), std::move(alignmentEditorFactory)); - - auto orientationEditorFactory = std::make_unique(); - orientationEditorFactory->setEnumNames({ - tr("Orthogonal"), - tr("Isometric"), - tr("Isometric (Staggered)"), - tr("Hexagonal (Staggered)"), - }); - orientationEditorFactory->setEnumValues({ - Map::Orthogonal, - Map::Isometric, - Map::Staggered, - Map::Hexagonal, - }); - registerEditorFactory(qMetaTypeId(), std::move(orientationEditorFactory)); - - auto staggerAxisEditorFactory = std::make_unique(); - staggerAxisEditorFactory->setEnumNames({ - tr("X"), - tr("Y"), - }); - registerEditorFactory(qMetaTypeId(), std::move(staggerAxisEditorFactory)); - - auto staggerIndexEditorFactory = std::make_unique(); - staggerIndexEditorFactory->setEnumNames({ - tr("Odd"), - tr("Even"), - }); - registerEditorFactory(qMetaTypeId(), std::move(staggerIndexEditorFactory)); + // auto alignmentEditorFactory = std::make_unique(); + // alignmentEditorFactory->setEnumNames({ + // tr("Unspecified"), + // tr("Top Left"), + // tr("Top"), + // tr("Top Right"), + // tr("Left"), + // tr("Center"), + // tr("Right"), + // tr("Bottom Left"), + // tr("Bottom"), + // tr("Bottom Right"), + // }); + // registerEditorFactory(qMetaTypeId(), std::move(alignmentEditorFactory)); + + + // auto staggerAxisEditorFactory = std::make_unique(); + // staggerAxisEditorFactory->setEnumNames({ + // tr("X"), + // tr("Y"), + // }); + // registerEditorFactory(qMetaTypeId(), std::move(staggerAxisEditorFactory)); + + // auto staggerIndexEditorFactory = std::make_unique(); + // staggerIndexEditorFactory->setEnumNames({ + // tr("Odd"), + // tr("Even"), + // }); + // registerEditorFactory(qMetaTypeId(), std::move(staggerIndexEditorFactory)); QStringList layerFormatNames = { QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), @@ -642,19 +552,19 @@ VariantEditor::VariantEditor(QWidget *parent) layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); layerFormatValues.append(Map::CSV); - auto layerFormatEditorFactory = std::make_unique(); - layerFormatEditorFactory->setEnumNames(layerFormatNames); - layerFormatEditorFactory->setEnumValues(layerFormatValues); - registerEditorFactory(qMetaTypeId(), std::move(layerFormatEditorFactory)); + // auto layerFormatEditorFactory = std::make_unique(); + // layerFormatEditorFactory->setEnumNames(layerFormatNames); + // layerFormatEditorFactory->setEnumValues(layerFormatValues); + // registerEditorFactory(qMetaTypeId(), std::move(layerFormatEditorFactory)); - auto renderOrderEditorFactory = std::make_unique(); - renderOrderEditorFactory->setEnumNames({ - tr("Right Down"), - tr("Right Up"), - tr("Left Down"), - tr("Left Up"), - }); - registerEditorFactory(qMetaTypeId(), std::move(renderOrderEditorFactory)); + // auto renderOrderEditorFactory = std::make_unique(); + // renderOrderEditorFactory->setEnumNames({ + // tr("Right Down"), + // tr("Right Up"), + // tr("Left Down"), + // tr("Left Up"), + // }); + // registerEditorFactory(qMetaTypeId(), std::move(renderOrderEditorFactory)); // setValue(QVariantMap { // { QStringLiteral("Name"), QVariant(QLatin1String("Hello")) }, @@ -698,11 +608,6 @@ VariantEditor::VariantEditor(QWidget *parent) // addHeader(tr("Custom Properties")); } -void VariantEditor::registerEditorFactory(int type, std::unique_ptr factory) -{ - m_factories[type] = std::move(factory); -} - void VariantEditor::clear() { QLayoutItem *item; @@ -785,13 +690,7 @@ void VariantEditor::addValue(const QVariant &value) QWidget *VariantEditor::createEditor(Property *property) { - const auto editor = property->createEditor(m_widget); - // const auto value = property->value(); - // const int type = value.userType(); - // auto factory = m_factories.find(type); - // if (factory != m_factories.end()) { - // const auto editor = factory->second->createEditor(value, m_widget); - if (editor) { + if (const auto editor = property->createEditor(m_widget)) { editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); return editor; } else { @@ -800,6 +699,222 @@ QWidget *VariantEditor::createEditor(Property *property) return nullptr; } +SizeEdit::SizeEdit(QWidget *parent) + : QWidget(parent) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_widthSpinBox(new SpinBox(this)) + , m_heightSpinBox(new SpinBox(this)) +{ + m_widthLabel->setAlignment(Qt::AlignCenter); + m_heightLabel->setAlignment(Qt::AlignCenter); + + auto layout = new QGridLayout(this); + layout->setContentsMargins(QMargins()); + layout->setColumnStretch(1, 1); + layout->setColumnStretch(3, 1); + layout->setSpacing(Utils::dpiScaled(3)); + + const int horizontalMargin = Utils::dpiScaled(3); + m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + + connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); + connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); +} + +void SizeEdit::setValue(const QSize &size) +{ + m_widthSpinBox->setValue(size.width()); + m_heightSpinBox->setValue(size.height()); +} + +QSize SizeEdit::value() const +{ + return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); +} + +void SizeEdit::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + + const auto orientation = event->size().width() < minimumHorizontalWidth() + ? Qt::Vertical : Qt::Horizontal; + + if (m_orientation != orientation) { + m_orientation = orientation; + + auto layout = qobject_cast(this->layout()); + + // Remove all widgets from layout, without deleting them + layout->removeWidget(m_widthLabel); + layout->removeWidget(m_widthSpinBox); + layout->removeWidget(m_heightLabel); + layout->removeWidget(m_heightSpinBox); + + if (orientation == Qt::Horizontal) { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + layout->setColumnStretch(3, 1); + } else { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 1, 0); + layout->addWidget(m_heightSpinBox, 1, 1); + layout->setColumnStretch(3, 0); + } + + // this avoids flickering when the layout changes + layout->activate(); + } +} + +int SizeEdit::minimumHorizontalWidth() const +{ + return m_widthLabel->minimumSizeHint().width() + + m_widthSpinBox->minimumSizeHint().width() + + m_heightLabel->minimumSizeHint().width() + + m_heightSpinBox->minimumSizeHint().width() + + layout()->spacing() * 3; +} + + +EnumEditorFactory::EnumEditorFactory(const QStringList &enumNames, + const QList &enumValues) + : m_enumNamesModel(enumNames) + , m_enumValues(enumValues) +{} + +void EnumEditorFactory::setEnumNames(const QStringList &enumNames) +{ + m_enumNamesModel.setStringList(enumNames); +} + +void EnumEditorFactory::setEnumValues(const QList &enumValues) +{ + m_enumValues = enumValues; +} + +QWidget *EnumEditorFactory::createEditor(Property *property, QWidget *parent) +{ + auto editor = new QComboBox(parent); + // This allows the combo box to shrink horizontally. + editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + editor->setModel(&m_enumNamesModel); + + auto syncEditor = [property, editor, this]() { + const QSignalBlocker blocker(editor); + if (m_enumValues.isEmpty()) + editor->setCurrentIndex(property->value().toInt()); + else + editor->setCurrentIndex(m_enumValues.indexOf(property->value().toInt())); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&QComboBox::currentIndexChanged), property, + [property, this](int index) { + property->setValue(m_enumValues.isEmpty() ? index : m_enumValues.at(index)); + }); + + return editor; +} + + +ValueTypeEditorFactory::ValueTypeEditorFactory() +{ + // Register some useful default editor factories + registerEditorFactory(QMetaType::Bool, std::make_unique()); + registerEditorFactory(QMetaType::Double, std::make_unique()); + registerEditorFactory(QMetaType::Int, std::make_unique()); + registerEditorFactory(QMetaType::QColor, std::make_unique()); + registerEditorFactory(QMetaType::QPoint, std::make_unique()); + registerEditorFactory(QMetaType::QPointF, std::make_unique()); + registerEditorFactory(QMetaType::QRectF, std::make_unique()); + registerEditorFactory(QMetaType::QSize, std::make_unique()); + registerEditorFactory(QMetaType::QString, std::make_unique()); +} + +void ValueTypeEditorFactory::registerEditorFactory(int type, std::unique_ptr factory) +{ + m_factories[type] = std::move(factory); +} + +QObjectProperty *ValueTypeEditorFactory::createQObjectProperty(QObject *qObject, + const char *name, + const QString &displayName) +{ + auto metaObject = qObject->metaObject(); + auto propertyIndex = metaObject->indexOfProperty(name); + if (propertyIndex < 0) + return nullptr; + + return new QObjectProperty(qObject, + metaObject->property(propertyIndex), + displayName.isEmpty() ? QString::fromUtf8(name) + : displayName, + this); +} + +ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, const QVariant &value) +{ + const int type = value.userType(); + auto factory = m_factories.find(type); + if (factory != m_factories.end()) + return new ValueProperty(name, value, factory->second.get()); + return nullptr; +} + +QWidget *ValueTypeEditorFactory::createEditor(Property *property, QWidget *parent) +{ + const auto value = property->value(); + const int type = value.userType(); + auto factory = m_factories.find(type); + if (factory != m_factories.end()) + return factory->second->createEditor(property, parent); + return nullptr; +} + + +QObjectProperty::QObjectProperty(QObject *object, + QMetaProperty property, + const QString &displayName, + EditorFactory *editorFactory, + QObject *parent) + : AbstractProperty(displayName, editorFactory, parent) + , m_object(object) + , m_property(property) +{ + // If the property has a notify signal, forward it to valueChanged + auto notify = property.notifySignal(); + if (notify.isValid()) { + auto valuePropertyIndex = metaObject()->indexOfProperty("value"); + auto valueProperty = metaObject()->property(valuePropertyIndex); + auto valueChanged = valueProperty.notifySignal(); + + connect(m_object, notify, this, valueChanged); + } + + setEnabled(m_property.isWritable()); +} + +QVariant QObjectProperty::value() const +{ + return m_property.read(m_object); +} + +void QObjectProperty::setValue(const QVariant &value) +{ + m_property.write(m_object, value); +} + } // namespace Tiled #include "moc_varianteditor.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 8fe48c725a..2057629da6 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include #include #include @@ -34,6 +35,9 @@ class QGridLayout; namespace Tiled { +/** + * A property represents a named value that can create its own edit widget. + */ class Property : public QObject { Q_OBJECT @@ -47,7 +51,7 @@ class Property : public QObject , m_name(name) {} - QString name() const { return m_name; } + const QString &name() const { return m_name; } bool isEnabled() const { return m_enabled; } void setEnabled(bool enabled) @@ -72,61 +76,168 @@ class Property : public QObject bool m_enabled = true; }; -class VariantProperty : public Property +/** + * An editor factory is responsible for creating an editor widget for a given + * property. It can be used to share the configuration of editor widgets + * between different properties. + */ +class EditorFactory +{ + Q_DECLARE_TR_FUNCTIONS(EditorFactory) + +public: + virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; +}; + +/** + * An editor factory that creates a combo box for enum properties. + */ +class EnumEditorFactory : public EditorFactory +{ +public: + EnumEditorFactory(const QStringList &enumNames = {}, + const QList &enumValues = {}); + + void setEnumNames(const QStringList &enumNames); + void setEnumValues(const QList &enumValues); + + QWidget *createEditor(Property *property, QWidget *parent) override; + +private: + QStringListModel m_enumNamesModel; + QList m_enumValues; +}; + +/** + * A property that uses an editor factory to create its editor, but does not + * store a value itself. + * + * The property does not take ownership of the editor factory. + */ +class AbstractProperty : public Property { Q_OBJECT public: - VariantProperty(const QString &name, const QVariant &value, QObject *parent = nullptr) - : Property(name, parent) - , m_value(value) - { - } + AbstractProperty(const QString &name, + EditorFactory *editorFactory, + QObject *parent = nullptr); + + QWidget *createEditor(QWidget *parent) override; + +private: + EditorFactory *m_editorFactory; +}; + +/** + * A property that stores a value of a given type and uses an editor factory to + * create its editor. + * + * The property does not take ownership of the editor factory. + */ +class ValueProperty : public AbstractProperty +{ + Q_OBJECT + +public: + ValueProperty(const QString &name, + const QVariant &value, + EditorFactory *editorFactory, + QObject *parent = nullptr); QVariant value() const override { return m_value; } void setValue(const QVariant &value) override; - QWidget *createEditor(QWidget *parent) override; - private: QVariant m_value; }; -class EnumProperty : public Property +/** + * A property that wraps a value of a QObject property and uses an editor + * factory to create its editor. + * + * The property does not take ownership of the editor factory. + */ +class QObjectProperty : public AbstractProperty { Q_OBJECT public: - EnumProperty(const QString &name, QObject *parent = nullptr) - : Property(name, parent) - {} + QObjectProperty(QObject *object, + QMetaProperty property, + const QString &displayName, + EditorFactory *editorFactory, + QObject *parent = nullptr); - QWidget *createEditor(QWidget *parent) override; + QVariant value() const override; + void setValue(const QVariant &value) override; - void setEnumNames(const QStringList &enumNames) - { - m_enumNamesModel.setStringList(enumNames); - } +private: + QObject *m_object; + QMetaProperty m_property; +}; - void setEnumValues(const QList &enumValues) - { - m_enumValues = enumValues; - } +/** + * An editor factory that selects the appropriate editor factory based on the + * type of the property value. + * + * todo: rename to VariantEditorFactory when the old one is removed + */ +class ValueTypeEditorFactory : public EditorFactory +{ +public: + ValueTypeEditorFactory(); + + /** + * Register an editor factory for a given type. + * + * When there is already an editor factory registered for the given type, + * it will be replaced. + */ + void registerEditorFactory(int type, std::unique_ptr factory); + + /** + * Creates a property that wraps a QObject property and will use the editor + * factory registered for the type of the value. + */ + QObjectProperty *createQObjectProperty(QObject *qObject, + const char *name, + const QString &displayName = {}); + + /** + * Creates a property with the given name and value. The property will use + * the editor factory registered for the type of the value. + */ + ValueProperty *createProperty(const QString &name, const QVariant &value); + + QWidget *createEditor(Property *property, QWidget *parent) override; private: - QStringListModel m_enumNamesModel; - QList m_enumValues; + std::unordered_map> m_factories; }; -class EditorFactory +/** + * A property that wraps an enum value and uses an editor factory to create + * its editor. + */ +class EnumProperty : public AbstractProperty { - Q_DECLARE_TR_FUNCTIONS(EditorFactory) + Q_OBJECT public: - virtual QWidget *createEditor(const QVariant &value, - QWidget *parent) = 0; + EnumProperty(const QString &name, + const QStringList &enumNames = {}, + const QList &enumValues = {}, + QObject *parent = nullptr); + + void setEnumNames(const QStringList &enumNames); + void setEnumValues(const QList &enumValues); + +private: + EnumEditorFactory m_editorFactory; }; + class VariantEditor : public QScrollArea { Q_OBJECT @@ -134,8 +245,6 @@ class VariantEditor : public QScrollArea public: VariantEditor(QWidget *parent = nullptr); - void registerEditorFactory(int type, std::unique_ptr factory); - void clear(); void addHeader(const QString &text); void addSeparator(); @@ -157,7 +266,6 @@ class VariantEditor : public QScrollArea QWidget *m_widget; QGridLayout *m_gridLayout; int m_rowIndex = 0; - std::unordered_map> m_factories; }; } // namespace Tiled From e40c924f64e0e8258a32ea33f756a56e417c1f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 2 Sep 2024 15:32:21 +0200 Subject: [PATCH 05/78] Implemented "Infinite" and "Hex Side Length" properties * Introduced MapProperties class to make it easier to emit valueChanged for all the map's properties, as well to move their creation out of PropertiesWidget::currentObjectChanged. * Added GetSetProperty that makes it easier to define a property in terms of a given getter and setter function, since it avoids the need for a specific Property subclass. * Finished implementation of the BoolEditorFactory (signals connected). * Moved property edit widgets to their own file. --- src/tiled/libtilededitor.qbs | 2 + src/tiled/propertieswidget.cpp | 146 ++++++++++--- src/tiled/propertieswidget.h | 3 +- src/tiled/propertyeditorwidgets.cpp | 230 +++++++++++++++++++++ src/tiled/propertyeditorwidgets.h | 109 ++++++++++ src/tiled/varianteditor.cpp | 310 +++------------------------- src/tiled/varianteditor.h | 36 +++- 7 files changed, 523 insertions(+), 313 deletions(-) create mode 100644 src/tiled/propertyeditorwidgets.cpp create mode 100644 src/tiled/propertyeditorwidgets.h diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 5e13f8bef3..4f6f7e8150 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -407,6 +407,8 @@ DynamicLibrary { "propertieswidget.h", "propertybrowser.cpp", "propertybrowser.h", + "propertyeditorwidgets.cpp", + "propertyeditorwidgets.h", "propertytypeseditor.cpp", "propertytypeseditor.h", "propertytypeseditor.ui", diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 0e7a882c01..9afabf0231 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -46,7 +46,6 @@ namespace Tiled { PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} - , mDocument(nullptr) , mPropertyBrowser(new VariantEditor(this)) , mDefaultEditorFactory(std::make_unique()) { @@ -150,8 +149,8 @@ class MapOrientationProperty : public EnumProperty Q_OBJECT public: - MapOrientationProperty(MapDocument *mapDocument) - : EnumProperty(tr("Orientation")) + MapOrientationProperty(MapDocument *mapDocument, QObject *parent = nullptr) + : EnumProperty(tr("Orientation"), parent) , mMapDocument(mapDocument) { setEnumNames({ @@ -166,9 +165,6 @@ class MapOrientationProperty : public EnumProperty Map::Staggered, Map::Hexagonal, }); - - connect(mMapDocument, &MapDocument::mapChanged, - this, &Property::valueChanged); } QVariant value() const override @@ -192,8 +188,9 @@ class MapSizeProperty : public AbstractProperty Q_OBJECT public: - MapSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory) - : AbstractProperty(tr("Map Size"), editorFactory) + MapSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory, + QObject *parent = nullptr) + : AbstractProperty(tr("Map Size"), editorFactory, parent) , mMapDocument(mapDocument) { connect(mMapDocument, &MapDocument::mapChanged, @@ -231,12 +228,12 @@ class TileSizeProperty : public AbstractProperty Q_OBJECT public: - TileSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory) - : AbstractProperty(tr("Tile Size"), editorFactory) + TileSizeProperty(MapDocument *mapDocument, + EditorFactory *editorFactory, + QObject *parent = nullptr) + : AbstractProperty(tr("Tile Size"), editorFactory, parent) , mMapDocument(mapDocument) { - connect(mMapDocument, &Document::changed, - this, &TileSizeProperty::onChanged); } QVariant value() const override @@ -265,17 +262,114 @@ class TileSizeProperty : public AbstractProperty }; private: - void onChanged(const ChangeEvent &event) + MapDocument *mMapDocument; +}; + +class MapProperties : public QObject +{ + Q_OBJECT + +public: + MapProperties(MapDocument *mapDocument, + ValueTypeEditorFactory *editorFactory, + QObject *parent = nullptr) + : QObject(parent) + , mMapDocument(mapDocument) + , mOrientationProperty(new MapOrientationProperty(mapDocument, this)) + , mSizeProperty(new MapSizeProperty(mapDocument, editorFactory, this)) + , mTileSizeProperty(new TileSizeProperty(mapDocument, editorFactory, this)) + { + mInfiniteProperty = editorFactory->createProperty( + tr("Infinite"), + [this]() { + return mMapDocument->map()->infinite(); + }, + [this](const QVariant &value) { + auto command = new ChangeMapProperty(mMapDocument, + Map::InfiniteProperty, + value.toBool()); + mMapDocument->undoStack()->push(command); + }); + + mHexSideLengthProperty = editorFactory->createProperty( + tr("Hex Side Length"), + [this]() { + return mMapDocument->map()->hexSideLength(); + }, + [this](const QVariant &value) { + auto command = new ChangeMapProperty(mMapDocument, + Map::HexSideLengthProperty, + value.toInt()); + mMapDocument->undoStack()->push(command); + }); + + connect(mMapDocument, &MapDocument::changed, + this, &MapProperties::onMapChanged); + } + + void populateEditor(VariantEditor *editor) + { + editor->addHeader(tr("Map")); + editor->addProperty(mOrientationProperty); + editor->addProperty(mSizeProperty); + editor->addProperty(mTileSizeProperty); + editor->addProperty(mInfiniteProperty); + editor->addProperty(mHexSideLengthProperty); + // editor->addProperty(mStaggerAxisProperty); + // editor->addProperty(mStaggerIndexProperty); + // editor->addProperty(mParallaxOriginProperty); + // editor->addProperty(mLayerDataFormatProperty); + // editor->addProperty(mChunkSizeProperty); + // editor->addProperty(mTileRenderOrderProperty); + // editor->addProperty(mCompressionLevelProperty); + // editor->addProperty(mBackgroundColorProperty); + } + +private: + void onMapChanged(const ChangeEvent &event) { if (event.type != ChangeEvent::MapChanged) return; const auto property = static_cast(event).property; - if (property == Map::TileWidthProperty || property == Map::TileHeightProperty) - emit valueChanged(); + switch (property) { + case Map::TileWidthProperty: + case Map::TileHeightProperty: + emit mTileSizeProperty->valueChanged(); + break; + case Map::InfiniteProperty: + emit mInfiniteProperty->valueChanged(); + break; + case Map::HexSideLengthProperty: + case Map::StaggerAxisProperty: + case Map::StaggerIndexProperty: + case Map::ParallaxOriginProperty: + case Map::OrientationProperty: + emit mOrientationProperty->valueChanged(); + break; + case Map::RenderOrderProperty: + case Map::BackgroundColorProperty: + case Map::LayerDataFormatProperty: + case Map::CompressionLevelProperty: + case Map::ChunkSizeProperty: + break; + } } MapDocument *mMapDocument; + Property *mOrientationProperty; + Property *mSizeProperty; + Property *mTileSizeProperty; + Property *mInfiniteProperty; + Property *mHexSideLengthProperty; + Property *mStaggerAxisProperty; + Property *mStaggerIndexProperty; + Property *mParallaxOriginProperty; + Property *mLayerDataFormatProperty; + Property *mChunkSizeProperty; + Property *mTileRenderOrderProperty; + Property *mCompressionLevelProperty; + Property *mBackgroundColorProperty; }; @@ -283,6 +377,8 @@ void PropertiesWidget::currentObjectChanged(Object *object) { // mPropertyBrowser->setObject(object); mPropertyBrowser->clear(); + delete mPropertiesObject; + mPropertiesObject = nullptr; if (object) { switch (object->typeId()) { @@ -290,24 +386,10 @@ void PropertiesWidget::currentObjectChanged(Object *object) case Object::MapObjectType: break; case Object::MapType: { - Map *map = static_cast(object); auto mapDocument = static_cast(mDocument); - - mPropertyBrowser->addHeader(tr("Map")); - mPropertyBrowser->addProperty(new MapOrientationProperty(mapDocument)); - mPropertyBrowser->addProperty(new MapSizeProperty(mapDocument, mDefaultEditorFactory.get())); - mPropertyBrowser->addProperty(new TileSizeProperty(mapDocument, mDefaultEditorFactory.get())); - // todo: infinite - // todo: hex side length - // todo: stagger axis - // todo: stagger index - // todo: parallax origin - // todo: layer data format - // todo: chunk size - // todo: tile render order - // todo: compression level - // todo: background color - + auto properties = new MapProperties(mapDocument, mDefaultEditorFactory.get(), this); + properties->populateEditor(mPropertyBrowser); + mPropertiesObject = properties; break; } case Object::TilesetType: diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index f9f5db095e..55447f7247 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -76,7 +76,8 @@ public slots: void retranslateUi(); - Document *mDocument; + Document *mDocument = nullptr; + QObject *mPropertiesObject = nullptr; VariantEditor *mPropertyBrowser; std::unique_ptr mDefaultEditorFactory; QAction *mActionAddProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp new file mode 100644 index 0000000000..36e899a3b0 --- /dev/null +++ b/src/tiled/propertyeditorwidgets.cpp @@ -0,0 +1,230 @@ +#include "propertyeditorwidgets.h" + +#include "utils.h" + +#include +#include +#include +#include +#include + +namespace Tiled { + +/** + * Strips a floating point number representation of redundant trailing zeros. + * Examples: + * + * 0.01000 -> 0.01 + * 3.000 -> 3.0 + */ +static QString removeRedundantTrialingZeros(const QString &text) +{ + const QString decimalPoint = QLocale::system().decimalPoint(); + const auto decimalPointIndex = text.lastIndexOf(decimalPoint); + if (decimalPointIndex < 0) // return if there is no decimal point + return text; + + const auto afterDecimalPoint = decimalPointIndex + decimalPoint.length(); + int redundantZeros = 0; + + for (int i = text.length() - 1; i > afterDecimalPoint && text.at(i) == QLatin1Char('0'); --i) + ++redundantZeros; + + return text.left(text.length() - redundantZeros); +} + + +SpinBox::SpinBox(QWidget *parent) + : QSpinBox(parent) +{ + // Allow the full range by default. + setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + + // Allow the widget to shrink horizontally. + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +QSize SpinBox::minimumSizeHint() const +{ + // Don't adjust the horizontal size hint based on the maximum value. + auto hint = QSpinBox::minimumSizeHint(); + hint.setWidth(Utils::dpiScaled(50)); + return hint; +} + + +DoubleSpinBox::DoubleSpinBox(QWidget *parent) + : QDoubleSpinBox(parent) +{ + // Allow the full range by default. + setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + + // Increase possible precision. + setDecimals(9); + + // Allow the widget to shrink horizontally. + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +QSize DoubleSpinBox::minimumSizeHint() const +{ + // Don't adjust the horizontal size hint based on the maximum value. + auto hint = QDoubleSpinBox::minimumSizeHint(); + hint.setWidth(Utils::dpiScaled(50)); + return hint; +} + +QString DoubleSpinBox::textFromValue(double val) const +{ + auto text = QDoubleSpinBox::textFromValue(val); + + // remove redundant trailing 0's in case of high precision + if (decimals() > 3) + return removeRedundantTrialingZeros(text); + + return text; +} + + +SizeEdit::SizeEdit(QWidget *parent) + : QWidget(parent) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_widthSpinBox(new SpinBox(this)) + , m_heightSpinBox(new SpinBox(this)) +{ + m_widthLabel->setAlignment(Qt::AlignCenter); + m_heightLabel->setAlignment(Qt::AlignCenter); + + auto layout = new QGridLayout(this); + layout->setContentsMargins(QMargins()); + layout->setColumnStretch(1, 1); + layout->setColumnStretch(3, 1); + layout->setSpacing(Utils::dpiScaled(3)); + + const int horizontalMargin = Utils::dpiScaled(3); + m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + + connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); + connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); +} + + +void SizeEdit::setValue(const QSize &size) +{ + m_widthSpinBox->setValue(size.width()); + m_heightSpinBox->setValue(size.height()); +} + +QSize SizeEdit::value() const +{ + return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); +} + +void SizeEdit::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + + const auto orientation = event->size().width() < minimumHorizontalWidth() + ? Qt::Vertical : Qt::Horizontal; + + if (m_orientation != orientation) { + m_orientation = orientation; + + auto layout = qobject_cast(this->layout()); + + // Remove all widgets from layout, without deleting them + layout->removeWidget(m_widthLabel); + layout->removeWidget(m_widthSpinBox); + layout->removeWidget(m_heightLabel); + layout->removeWidget(m_heightSpinBox); + + if (orientation == Qt::Horizontal) { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 0, 2); + layout->addWidget(m_heightSpinBox, 0, 3); + layout->setColumnStretch(3, 1); + } else { + layout->addWidget(m_widthLabel, 0, 0); + layout->addWidget(m_widthSpinBox, 0, 1); + layout->addWidget(m_heightLabel, 1, 0); + layout->addWidget(m_heightSpinBox, 1, 1); + layout->setColumnStretch(3, 0); + } + + // this avoids flickering when the layout changes + layout->activate(); + } +} + +int SizeEdit::minimumHorizontalWidth() const +{ + return m_widthLabel->minimumSizeHint().width() + + m_widthSpinBox->minimumSizeHint().width() + + m_heightLabel->minimumSizeHint().width() + + m_heightSpinBox->minimumSizeHint().width() + + layout()->spacing() * 3; +} + + +ElidingLabel::ElidingLabel(QWidget *parent) + : ElidingLabel(QString(), parent) +{} + +ElidingLabel::ElidingLabel(const QString &text, QWidget *parent) + : QLabel(text, parent) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); +} + +QSize ElidingLabel::minimumSizeHint() const +{ + auto hint = QLabel::minimumSizeHint(); + hint.setWidth(std::min(hint.width(), Utils::dpiScaled(30))); + return hint; +} + +void ElidingLabel::paintEvent(QPaintEvent *) +{ + const int m = margin(); + const QRect cr = contentsRect().adjusted(m, m, -m, -m); + const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; + const int align = QStyle::visualAlignment(dir, alignment()); + const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight + : Qt::TextForceRightToLeft); + + QStyleOption opt; + opt.initFrom(this); + + const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); + const bool isElided = elidedText != text(); + + if (isElided != m_isElided) { + m_isElided = isElided; + setToolTip(isElided ? text() : QString()); + } + + QPainter painter(this); + QWidget::style()->drawItemText(&painter, cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); +} + + +QSize LineEditLabel::sizeHint() const +{ + auto hint = ElidingLabel::sizeHint(); + hint.setHeight(m_lineEdit.sizeHint().height()); + return hint; +} + +} // namespace Tiled + +#include "moc_propertyeditorwidgets.cpp" diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h new file mode 100644 index 0000000000..f33eaf7fbd --- /dev/null +++ b/src/tiled/propertyeditorwidgets.h @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include + +class QLabel; + +namespace Tiled { + +/** + * A spin box that allows the full range by default and shrinks horizontally. + * It also doesn't adjust the horizontal size hint based on the maximum value. + */ +class SpinBox : public QSpinBox +{ + Q_OBJECT + +public: + SpinBox(QWidget *parent = nullptr); + + QSize minimumSizeHint() const override; +}; + +/** + * A double spin box that allows the full range by default and shrinks + * horizontally. It also doesn't adjust the horizontal size hint based on the + * maximum value. + * + * The precision is increased to 9 decimal places. Redundant trailing 0's are + * removed. + */ +class DoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT + +public: + DoubleSpinBox(QWidget *parent = nullptr); + + QSize minimumSizeHint() const override; + QString textFromValue(double val) const override; +}; + +/** + * A widget for editing a QSize value. + */ +class SizeEdit : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QSize value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + SizeEdit(QWidget *parent = nullptr); + + void setValue(const QSize &size); + QSize value() const; + +signals: + void valueChanged(); + +private: + void resizeEvent(QResizeEvent *event) override; + + int minimumHorizontalWidth() const; + + Qt::Orientation m_orientation = Qt::Horizontal; + QLabel *m_widthLabel; + QLabel *m_heightLabel; + SpinBox *m_widthSpinBox; + SpinBox *m_heightSpinBox; +}; + +/** + * A label that elides its text if there is not enough space. + * + * The elided text is shown as a tooltip. + */ +class ElidingLabel : public QLabel +{ + Q_OBJECT + +public: + explicit ElidingLabel(QWidget *parent = nullptr); + ElidingLabel(const QString &text, QWidget *parent = nullptr); + + QSize minimumSizeHint() const override; + void paintEvent(QPaintEvent *) override; + +private: + bool m_isElided = false; +}; + +/** + * A label that matches its preferred height with that of a line edit. + */ +class LineEditLabel : public ElidingLabel +{ + Q_OBJECT + +public: + using ElidingLabel::ElidingLabel; + + QSize sizeHint() const override; + +private: + QLineEdit m_lineEdit; +}; + +} // namespace Tiled diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 60d2290417..523ad504c4 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -19,10 +19,12 @@ */ #include "varianteditor.h" + #include "colorbutton.h" #include "compression.h" #include "map.h" #include "utils.h" +#include "propertyeditorwidgets.h" #include #include @@ -51,164 +53,17 @@ QWidget *AbstractProperty::createEditor(QWidget *parent) } -class SpinBox : public QSpinBox -{ - Q_OBJECT - -public: - SpinBox(QWidget *parent = nullptr) - : QSpinBox(parent) - { - // Allow the full range by default. - setRange(std::numeric_limits::lowest(), - std::numeric_limits::max()); - - // Allow the widget to shrink horizontally. - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - } - - QSize minimumSizeHint() const override - { - // Don't adjust the horizontal size hint based on the maximum value. - auto hint = QSpinBox::minimumSizeHint(); - hint.setWidth(Utils::dpiScaled(50)); - return hint; - } -}; - -/** - * Strips a floating point number representation of redundant trailing zeros. - * Examples: - * - * 0.01000 -> 0.01 - * 3.000 -> 3.0 - */ -QString removeRedundantTrialingZeros(const QString &text) -{ - const QString decimalPoint = QLocale::system().decimalPoint(); - const auto decimalPointIndex = text.lastIndexOf(decimalPoint); - if (decimalPointIndex < 0) // return if there is no decimal point - return text; - - const auto afterDecimalPoint = decimalPointIndex + decimalPoint.length(); - int redundantZeros = 0; - - for (int i = text.length() - 1; i > afterDecimalPoint && text.at(i) == QLatin1Char('0'); --i) - ++redundantZeros; - - return text.left(text.length() - redundantZeros); -} - -class DoubleSpinBox : public QDoubleSpinBox -{ - Q_OBJECT - -public: - DoubleSpinBox(QWidget *parent = nullptr) - : QDoubleSpinBox(parent) - { - // Allow the full range by default. - setRange(std::numeric_limits::lowest(), - std::numeric_limits::max()); - - // Increase possible precision. - setDecimals(9); - - // Allow the widget to shrink horizontally. - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - } - - QSize minimumSizeHint() const override - { - // Don't adjust the horizontal size hint based on the maximum value. - auto hint = QDoubleSpinBox::minimumSizeHint(); - hint.setWidth(Utils::dpiScaled(50)); - return hint; - } - - // QDoubleSpinBox interface - QString textFromValue(double val) const override - { - auto text = QDoubleSpinBox::textFromValue(val); - - // remove redundant trailing 0's in case of high precision - if (decimals() > 3) - return removeRedundantTrialingZeros(text); - - return text; - } -}; - - -// A label that elides its text if there is not enough space -class ElidingLabel : public QLabel -{ - Q_OBJECT - -public: - explicit ElidingLabel(QWidget *parent = nullptr) - : ElidingLabel(QString(), parent) - {} - - ElidingLabel(const QString &text, QWidget *parent = nullptr) - : QLabel(text, parent) - { - setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); - } - - QSize minimumSizeHint() const override - { - auto hint = QLabel::minimumSizeHint(); - hint.setWidth(std::min(hint.width(), Utils::dpiScaled(30))); - return hint; - } - - void paintEvent(QPaintEvent *) override - { - const int m = margin(); - const QRect cr = contentsRect().adjusted(m, m, -m, -m); - const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; - const int align = QStyle::visualAlignment(dir, alignment()); - const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight - : Qt::TextForceRightToLeft); - - QStyleOption opt; - opt.initFrom(this); - - const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); - - const bool isElided = elidedText != text(); - if (isElided != m_isElided) { - m_isElided = isElided; - setToolTip(isElided ? text() : QString()); - } - - QPainter painter(this); - QWidget::style()->drawItemText(&painter, cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); - } - -private: - bool m_isElided = false; -}; - -// A label that matches its preferred height with that of a line edit -class LineEditLabel : public ElidingLabel -{ - Q_OBJECT +GetSetProperty::GetSetProperty(const QString &name, + std::function get, + std::function set, + EditorFactory *editorFactory, + QObject *parent) + : AbstractProperty(name, editorFactory, parent) + , m_get(std::move(get)) + , m_set(std::move(set)) +{} -public: - using ElidingLabel::ElidingLabel; - QSize sizeHint() const override - { - auto hint = ElidingLabel::sizeHint(); - hint.setHeight(lineEdit.sizeHint().height()); - return hint; - } - -private: - QLineEdit lineEdit; -}; class StringEditorFactory : public EditorFactory { @@ -251,14 +106,19 @@ class BoolEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new QCheckBox(parent); - bool checked = value.toBool(); - editor->setChecked(checked); - editor->setText(checked ? tr("On") : tr("Off")); + auto syncEditor = [=]() { + const QSignalBlocker blocker(editor); + bool checked = property->value().toBool(); + editor->setChecked(checked); + editor->setText(checked ? tr("On") : tr("Off")); + }; + syncEditor(); - QObject::connect(editor, &QCheckBox::toggled, [editor](bool checked) { + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &QCheckBox::toggled, property, [=](bool checked) { editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); + property->setValue(checked); }); return editor; @@ -327,34 +187,6 @@ class PointFEditorFactory : public EditorFactory } }; -/** - * A widget for editing a QSize value. - */ -class SizeEdit : public QWidget -{ - Q_OBJECT - Q_PROPERTY(QSize value READ value WRITE setValue NOTIFY valueChanged FINAL) - -public: - SizeEdit(QWidget *parent = nullptr); - - void setValue(const QSize &size); - QSize value() const; - -signals: - void valueChanged(); - -private: - void resizeEvent(QResizeEvent *event) override; - - int minimumHorizontalWidth() const; - - Qt::Orientation m_orientation = Qt::Horizontal; - QLabel *m_widthLabel; - QLabel *m_heightLabel; - SpinBox *m_widthSpinBox; - SpinBox *m_heightSpinBox; -}; class SizeEditorFactory : public EditorFactory { @@ -460,11 +292,8 @@ void ValueProperty::setValue(const QVariant &value) EnumProperty::EnumProperty(const QString &name, - const QStringList &enumNames, - const QList &enumValues, QObject *parent) : AbstractProperty(name, &m_editorFactory, parent) - , m_editorFactory(enumNames, enumValues) {} void EnumProperty::setEnumNames(const QStringList &enumNames) @@ -699,91 +528,6 @@ QWidget *VariantEditor::createEditor(Property *property) return nullptr; } -SizeEdit::SizeEdit(QWidget *parent) - : QWidget(parent) - , m_widthLabel(new QLabel(QStringLiteral("W"), this)) - , m_heightLabel(new QLabel(QStringLiteral("H"), this)) - , m_widthSpinBox(new SpinBox(this)) - , m_heightSpinBox(new SpinBox(this)) -{ - m_widthLabel->setAlignment(Qt::AlignCenter); - m_heightLabel->setAlignment(Qt::AlignCenter); - - auto layout = new QGridLayout(this); - layout->setContentsMargins(QMargins()); - layout->setColumnStretch(1, 1); - layout->setColumnStretch(3, 1); - layout->setSpacing(Utils::dpiScaled(3)); - - const int horizontalMargin = Utils::dpiScaled(3); - m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); - - connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); - connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); -} - -void SizeEdit::setValue(const QSize &size) -{ - m_widthSpinBox->setValue(size.width()); - m_heightSpinBox->setValue(size.height()); -} - -QSize SizeEdit::value() const -{ - return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); -} - -void SizeEdit::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - - const auto orientation = event->size().width() < minimumHorizontalWidth() - ? Qt::Vertical : Qt::Horizontal; - - if (m_orientation != orientation) { - m_orientation = orientation; - - auto layout = qobject_cast(this->layout()); - - // Remove all widgets from layout, without deleting them - layout->removeWidget(m_widthLabel); - layout->removeWidget(m_widthSpinBox); - layout->removeWidget(m_heightLabel); - layout->removeWidget(m_heightSpinBox); - - if (orientation == Qt::Horizontal) { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); - layout->setColumnStretch(3, 1); - } else { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 1, 0); - layout->addWidget(m_heightSpinBox, 1, 1); - layout->setColumnStretch(3, 0); - } - - // this avoids flickering when the layout changes - layout->activate(); - } -} - -int SizeEdit::minimumHorizontalWidth() const -{ - return m_widthLabel->minimumSizeHint().width() + - m_widthSpinBox->minimumSizeHint().width() + - m_heightLabel->minimumSizeHint().width() + - m_heightSpinBox->minimumSizeHint().width() + - layout()->spacing() * 3; -} EnumEditorFactory::EnumEditorFactory(const QStringList &enumNames, @@ -872,6 +616,17 @@ ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, const return nullptr; } +AbstractProperty *ValueTypeEditorFactory::createProperty(const QString &name, + std::function get, + std::function set) +{ + const int type = get().userType(); + auto factory = m_factories.find(type); + if (factory != m_factories.end()) + return new GetSetProperty(name, get, set, factory->second.get()); + return nullptr; +} + QWidget *ValueTypeEditorFactory::createEditor(Property *property, QWidget *parent) { const auto value = property->value(); @@ -918,4 +673,3 @@ void QObjectProperty::setValue(const QVariant &value) } // namespace Tiled #include "moc_varianteditor.cpp" -#include "varianteditor.moc" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 2057629da6..8099181347 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -129,6 +129,31 @@ class AbstractProperty : public Property EditorFactory *m_editorFactory; }; +/** + * A property that uses the given functions to get and set the value and uses + * an editor factory to create its editor. + * + * The property does not take ownership of the editor factory. + */ +class GetSetProperty : public AbstractProperty +{ + Q_OBJECT + +public: + GetSetProperty(const QString &name, + std::function get, + std::function set, + EditorFactory *editorFactory, + QObject *parent = nullptr); + + QVariant value() const override { return m_get(); } + void setValue(const QVariant &value) override { m_set(value); } + +private: + std::function m_get; + std::function m_set; +}; + /** * A property that stores a value of a given type and uses an editor factory to * create its editor. @@ -210,6 +235,15 @@ class ValueTypeEditorFactory : public EditorFactory */ ValueProperty *createProperty(const QString &name, const QVariant &value); + /** + * Creates a property with the given name and get/set functions. The + * property will use the editor factory registered for the type of the + * value. + */ + AbstractProperty *createProperty(const QString &name, + std::function get, + std::function set); + QWidget *createEditor(Property *property, QWidget *parent) override; private: @@ -226,8 +260,6 @@ class EnumProperty : public AbstractProperty public: EnumProperty(const QString &name, - const QStringList &enumNames = {}, - const QList &enumValues = {}, QObject *parent = nullptr); void setEnumNames(const QStringList &enumNames); From 21259d3e923176c7c3bd3ea5b3f38acf5259775d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 2 Sep 2024 16:43:11 +0200 Subject: [PATCH 06/78] Implemented "Stagger Axis/Index" properties --- src/tiled/propertieswidget.cpp | 107 ++++++++++++++++++++++++++++++++- src/tiled/propertieswidget.h | 4 ++ src/tiled/varianteditor.cpp | 91 ++++------------------------ 3 files changed, 122 insertions(+), 80 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 9afabf0231..3515bfc81e 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -25,6 +25,7 @@ #include "changemapproperty.h" #include "changeproperties.h" #include "clipboardmanager.h" +#include "compression.h" #include "mapdocument.h" #include "propertybrowser.h" #include "utils.h" @@ -93,6 +94,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, // this, &PropertiesWidget::updateActions); + registerEditorFactories(); retranslateUi(); } @@ -303,6 +305,30 @@ class MapProperties : public QObject mMapDocument->undoStack()->push(command); }); + mStaggerAxisProperty = editorFactory->createProperty( + tr("Stagger Axis"), + [this]() { + return QVariant::fromValue(mMapDocument->map()->staggerAxis()); + }, + [this](const QVariant &value) { + auto command = new ChangeMapProperty(mMapDocument, + Map::StaggerAxisProperty, + value.toInt()); + mMapDocument->undoStack()->push(command); + }); + + mStaggerIndexProperty = editorFactory->createProperty( + tr("Stagger Index"), + [this]() { + return QVariant::fromValue(mMapDocument->map()->staggerIndex()); + }, + [this](const QVariant &value) { + auto command = new ChangeMapProperty(mMapDocument, + Map::StaggerIndexProperty, + value.toInt()); + mMapDocument->undoStack()->push(command); + }); + connect(mMapDocument, &MapDocument::changed, this, &MapProperties::onMapChanged); } @@ -315,8 +341,8 @@ class MapProperties : public QObject editor->addProperty(mTileSizeProperty); editor->addProperty(mInfiniteProperty); editor->addProperty(mHexSideLengthProperty); - // editor->addProperty(mStaggerAxisProperty); - // editor->addProperty(mStaggerIndexProperty); + editor->addProperty(mStaggerAxisProperty); + editor->addProperty(mStaggerIndexProperty); // editor->addProperty(mParallaxOriginProperty); // editor->addProperty(mLayerDataFormatProperty); // editor->addProperty(mChunkSizeProperty); @@ -341,8 +367,14 @@ class MapProperties : public QObject emit mInfiniteProperty->valueChanged(); break; case Map::HexSideLengthProperty: + emit mHexSideLengthProperty->valueChanged(); + break; case Map::StaggerAxisProperty: + emit mStaggerAxisProperty->valueChanged(); + break; case Map::StaggerIndexProperty: + emit mStaggerIndexProperty->valueChanged(); + break; case Map::ParallaxOriginProperty: case Map::OrientationProperty: emit mOrientationProperty->valueChanged(); @@ -782,6 +814,77 @@ void PropertiesWidget::keyPressEvent(QKeyEvent *event) } } +void PropertiesWidget::registerEditorFactories() +{ + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Unspecified"), + tr("Top Left"), + tr("Top"), + tr("Top Right"), + tr("Left"), + tr("Center"), + tr("Right"), + tr("Bottom Left"), + tr("Bottom"), + tr("Bottom Right"), + })); + + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("X"), + tr("Y"), + })); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Odd"), + tr("Even"), + })); + + QStringList layerFormatNames = { + QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), + }; + QList layerFormatValues = { + Map::XML, + Map::Base64, + Map::Base64Gzip, + Map::Base64Zlib, + }; + + if (compressionSupported(Zstandard)) { + layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); + layerFormatValues.append(Map::Base64Zstandard); + } + + layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); + layerFormatValues.append(Map::CSV); + + registerEditorFactory(qMetaTypeId(), + std::make_unique(layerFormatNames, layerFormatValues)); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Right Down"), + tr("Right Up"), + tr("Left Down"), + tr("Left Up"), + })); +} + +void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) +{ + mDefaultEditorFactory->registerEditorFactory(type, std::move(factory)); +} + void PropertiesWidget::retranslateUi() { mActionAddProperty->setText(QCoreApplication::translate("Tiled::PropertiesDock", "Add Property")); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 55447f7247..37db77ee6d 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -29,6 +29,7 @@ namespace Tiled { class Object; class Document; +class EditorFactory; class ValueTypeEditorFactory; class VariantEditor; @@ -61,6 +62,9 @@ public slots: void keyPressEvent(QKeyEvent *event) override; private: + void registerEditorFactories(); + void registerEditorFactory(int type, std::unique_ptr factory); + void currentObjectChanged(Object *object); void updateActions(); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 523ad504c4..081f3f8f95 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -49,7 +49,8 @@ AbstractProperty::AbstractProperty(const QString &name, QWidget *AbstractProperty::createEditor(QWidget *parent) { - return m_editorFactory->createEditor(this, parent); + return m_editorFactory ? m_editorFactory->createEditor(this, parent) + : nullptr; } @@ -316,7 +317,7 @@ VariantEditor::VariantEditor(QWidget *parent) m_gridLayout = new QGridLayout; verticalLayout->addLayout(m_gridLayout); verticalLayout->addStretch(); - verticalLayout->setContentsMargins(QMargins()); + verticalLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(6)); setWidget(m_widget); setWidgetResizable(true); @@ -330,71 +331,6 @@ VariantEditor::VariantEditor(QWidget *parent) m_gridLayout->setColumnMinimumWidth(MiddleSpacing, Utils::dpiScaled(2)); m_gridLayout->setColumnMinimumWidth(RightSpacing, Utils::dpiScaled(3)); - // auto alignmentEditorFactory = std::make_unique(); - // alignmentEditorFactory->setEnumNames({ - // tr("Unspecified"), - // tr("Top Left"), - // tr("Top"), - // tr("Top Right"), - // tr("Left"), - // tr("Center"), - // tr("Right"), - // tr("Bottom Left"), - // tr("Bottom"), - // tr("Bottom Right"), - // }); - // registerEditorFactory(qMetaTypeId(), std::move(alignmentEditorFactory)); - - - // auto staggerAxisEditorFactory = std::make_unique(); - // staggerAxisEditorFactory->setEnumNames({ - // tr("X"), - // tr("Y"), - // }); - // registerEditorFactory(qMetaTypeId(), std::move(staggerAxisEditorFactory)); - - // auto staggerIndexEditorFactory = std::make_unique(); - // staggerIndexEditorFactory->setEnumNames({ - // tr("Odd"), - // tr("Even"), - // }); - // registerEditorFactory(qMetaTypeId(), std::move(staggerIndexEditorFactory)); - - QStringList layerFormatNames = { - QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), - }; - QList layerFormatValues = { - Map::XML, - Map::Base64, - Map::Base64Gzip, - Map::Base64Zlib, - }; - - if (compressionSupported(Zstandard)) { - layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); - layerFormatValues.append(Map::Base64Zstandard); - } - - layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); - layerFormatValues.append(Map::CSV); - - // auto layerFormatEditorFactory = std::make_unique(); - // layerFormatEditorFactory->setEnumNames(layerFormatNames); - // layerFormatEditorFactory->setEnumValues(layerFormatValues); - // registerEditorFactory(qMetaTypeId(), std::move(layerFormatEditorFactory)); - - // auto renderOrderEditorFactory = std::make_unique(); - // renderOrderEditorFactory->setEnumNames({ - // tr("Right Down"), - // tr("Right Up"), - // tr("Left Down"), - // tr("Left Up"), - // }); - // registerEditorFactory(qMetaTypeId(), std::move(renderOrderEditorFactory)); - // setValue(QVariantMap { // { QStringLiteral("Name"), QVariant(QLatin1String("Hello")) }, // { QStringLiteral("Position"), QVariant(QPoint(15, 50)) }, @@ -607,24 +543,23 @@ QObjectProperty *ValueTypeEditorFactory::createQObjectProperty(QObject *qObject, this); } -ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, const QVariant &value) +ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, + const QVariant &value) { - const int type = value.userType(); - auto factory = m_factories.find(type); - if (factory != m_factories.end()) - return new ValueProperty(name, value, factory->second.get()); - return nullptr; + auto f = m_factories.find(value.userType()); + return new ValueProperty(name, value, + f != m_factories.end() ? f->second.get() + : nullptr); } AbstractProperty *ValueTypeEditorFactory::createProperty(const QString &name, std::function get, std::function set) { - const int type = get().userType(); - auto factory = m_factories.find(type); - if (factory != m_factories.end()) - return new GetSetProperty(name, get, set, factory->second.get()); - return nullptr; + auto f = m_factories.find(get().userType()); + return new GetSetProperty(name, get, set, + f != m_factories.end() ? f->second.get() + : nullptr); } QWidget *ValueTypeEditorFactory::createEditor(Property *property, QWidget *parent) From 0ee9d102b6135f6f90d7643e8396b00c72602aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 3 Sep 2024 17:57:17 +0200 Subject: [PATCH 07/78] Implemented remaining Map properties * Introduced a few helper functions to reduce code duplication, like MapProperties::push. * Disabled properties when they are irrelevant. * Finished connecting the signals for the remaining editor factories: StringEditorFactory, IntEditorFactory, FloatEditorFactory, PointEditorFactory, PointFEditorFactory, RectFEditorFactory and ColorEditorFactory. --- src/libtiled/map.h | 2 + src/tiled/propertieswidget.cpp | 236 +++++++++++++++++++++++---------- src/tiled/varianteditor.cpp | 155 +++++++++++++++------- 3 files changed, 276 insertions(+), 117 deletions(-) diff --git a/src/libtiled/map.h b/src/libtiled/map.h index 0abee3233e..b7e58117fd 100644 --- a/src/libtiled/map.h +++ b/src/libtiled/map.h @@ -791,3 +791,5 @@ Q_DECLARE_METATYPE(Tiled::Map*) Q_DECLARE_METATYPE(Tiled::Map::Orientation) Q_DECLARE_METATYPE(Tiled::Map::LayerDataFormat) Q_DECLARE_METATYPE(Tiled::Map::RenderOrder) +Q_DECLARE_METATYPE(Tiled::Map::StaggerAxis) +Q_DECLARE_METATYPE(Tiled::Map::StaggerIndex) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 3515bfc81e..21e1ff3f46 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -146,45 +146,6 @@ static bool anyObjectHasProperty(const QList &objects, const QString &n return false; } -class MapOrientationProperty : public EnumProperty -{ - Q_OBJECT - -public: - MapOrientationProperty(MapDocument *mapDocument, QObject *parent = nullptr) - : EnumProperty(tr("Orientation"), parent) - , mMapDocument(mapDocument) - { - setEnumNames({ - tr("Orthogonal"), - tr("Isometric"), - tr("Isometric (Staggered)"), - tr("Hexagonal (Staggered)"), - }); - setEnumValues({ - Map::Orthogonal, - Map::Isometric, - Map::Staggered, - Map::Hexagonal, - }); - } - - QVariant value() const override - { - return mMapDocument->map()->orientation(); - } - - void setValue(const QVariant &value) override - { - Map::Orientation orientation = static_cast(value.toInt()); - auto command = new ChangeMapProperty(mMapDocument, orientation); - mMapDocument->undoStack()->push(command); - } - -private: - MapDocument *mMapDocument; -}; - class MapSizeProperty : public AbstractProperty { Q_OBJECT @@ -277,58 +238,128 @@ class MapProperties : public QObject QObject *parent = nullptr) : QObject(parent) , mMapDocument(mapDocument) - , mOrientationProperty(new MapOrientationProperty(mapDocument, this)) , mSizeProperty(new MapSizeProperty(mapDocument, editorFactory, this)) , mTileSizeProperty(new TileSizeProperty(mapDocument, editorFactory, this)) { + mClassProperty = editorFactory->createProperty( + tr("Class"), + [this]() { + return map()->className(); + }, + [this](const QVariant &value) { + push(new ChangeClassName(mMapDocument, { map() }, + value.toString())); + }); + + mOrientationProperty = editorFactory->createProperty( + tr("Orientation"), + [this]() { + return QVariant::fromValue(map()->orientation()); + }, + [this](const QVariant &value) { + auto orientation = static_cast(value.toInt()); + push(new ChangeMapProperty(mMapDocument, orientation)); + }); + mInfiniteProperty = editorFactory->createProperty( tr("Infinite"), [this]() { - return mMapDocument->map()->infinite(); + return map()->infinite(); }, [this](const QVariant &value) { - auto command = new ChangeMapProperty(mMapDocument, - Map::InfiniteProperty, - value.toBool()); - mMapDocument->undoStack()->push(command); + push(new ChangeMapProperty(mMapDocument, + Map::InfiniteProperty, + value.toInt())); }); mHexSideLengthProperty = editorFactory->createProperty( tr("Hex Side Length"), [this]() { - return mMapDocument->map()->hexSideLength(); + return map()->hexSideLength(); }, [this](const QVariant &value) { - auto command = new ChangeMapProperty(mMapDocument, - Map::HexSideLengthProperty, - value.toInt()); - mMapDocument->undoStack()->push(command); + push(new ChangeMapProperty(mMapDocument, + Map::HexSideLengthProperty, + value.toInt())); }); mStaggerAxisProperty = editorFactory->createProperty( tr("Stagger Axis"), [this]() { - return QVariant::fromValue(mMapDocument->map()->staggerAxis()); + return QVariant::fromValue(map()->staggerAxis()); }, [this](const QVariant &value) { - auto command = new ChangeMapProperty(mMapDocument, - Map::StaggerAxisProperty, - value.toInt()); - mMapDocument->undoStack()->push(command); + auto staggerAxis = static_cast(value.toInt()); + push(new ChangeMapProperty(mMapDocument, staggerAxis)); }); mStaggerIndexProperty = editorFactory->createProperty( tr("Stagger Index"), [this]() { - return QVariant::fromValue(mMapDocument->map()->staggerIndex()); + return QVariant::fromValue(map()->staggerIndex()); }, [this](const QVariant &value) { - auto command = new ChangeMapProperty(mMapDocument, - Map::StaggerIndexProperty, - value.toInt()); - mMapDocument->undoStack()->push(command); + auto staggerIndex = static_cast(value.toInt()); + push(new ChangeMapProperty(mMapDocument, staggerIndex)); }); + mParallaxOriginProperty = editorFactory->createProperty( + tr("Parallax Origin"), + [this]() { + return map()->parallaxOrigin(); + }, + [this](const QVariant &value) { + push(new ChangeMapProperty(mMapDocument, value.value())); + }); + + mLayerDataFormatProperty = editorFactory->createProperty( + tr("Layer Data Format"), + [this]() { + return QVariant::fromValue(map()->layerDataFormat()); + }, + [this](const QVariant &value) { + auto layerDataFormat = static_cast(value.toInt()); + push(new ChangeMapProperty(mMapDocument, layerDataFormat)); + }); + + mChunkSizeProperty = editorFactory->createProperty( + tr("Output Chunk Size"), + [this]() { + return map()->chunkSize(); + }, + [this](const QVariant &value) { + push(new ChangeMapProperty(mMapDocument, value.toSize())); + }); + + mRenderOrderProperty = editorFactory->createProperty( + tr("Tile Render Order"), + [this]() { + return QVariant::fromValue(map()->renderOrder()); + }, + [this](const QVariant &value) { + auto renderOrder = static_cast(value.toInt()); + push(new ChangeMapProperty(mMapDocument, renderOrder)); + }); + + mCompressionLevelProperty = editorFactory->createProperty( + tr("Compression Level"), + [this]() { + return map()->compressionLevel(); + }, + [this](const QVariant &value) { + push(new ChangeMapProperty(mMapDocument, value.toInt())); + }); + + mBackgroundColorProperty = editorFactory->createProperty( + tr("Background Color"), + [this]() { + return map()->backgroundColor(); + }, + [this](const QVariant &value) { + push(new ChangeMapProperty(mMapDocument, value.value())); + }); + + updateEnabledState(); connect(mMapDocument, &MapDocument::changed, this, &MapProperties::onMapChanged); } @@ -336,19 +367,24 @@ class MapProperties : public QObject void populateEditor(VariantEditor *editor) { editor->addHeader(tr("Map")); + editor->addProperty(mClassProperty); + editor->addSeparator(); editor->addProperty(mOrientationProperty); editor->addProperty(mSizeProperty); - editor->addProperty(mTileSizeProperty); editor->addProperty(mInfiniteProperty); + editor->addProperty(mTileSizeProperty); editor->addProperty(mHexSideLengthProperty); editor->addProperty(mStaggerAxisProperty); editor->addProperty(mStaggerIndexProperty); - // editor->addProperty(mParallaxOriginProperty); - // editor->addProperty(mLayerDataFormatProperty); - // editor->addProperty(mChunkSizeProperty); - // editor->addProperty(mTileRenderOrderProperty); - // editor->addProperty(mCompressionLevelProperty); - // editor->addProperty(mBackgroundColorProperty); + editor->addSeparator(); + editor->addProperty(mParallaxOriginProperty); + editor->addSeparator(); + editor->addProperty(mLayerDataFormatProperty); + editor->addProperty(mChunkSizeProperty); + editor->addProperty(mCompressionLevelProperty); + editor->addSeparator(); + editor->addProperty(mRenderOrderProperty); + editor->addProperty(mBackgroundColorProperty); } private: @@ -376,19 +412,68 @@ class MapProperties : public QObject emit mStaggerIndexProperty->valueChanged(); break; case Map::ParallaxOriginProperty: + emit mParallaxOriginProperty->valueChanged(); + break; case Map::OrientationProperty: emit mOrientationProperty->valueChanged(); break; case Map::RenderOrderProperty: + emit mRenderOrderProperty->valueChanged(); + break; case Map::BackgroundColorProperty: + emit mBackgroundColorProperty->valueChanged(); + break; case Map::LayerDataFormatProperty: + emit mLayerDataFormatProperty->valueChanged(); + break; case Map::CompressionLevelProperty: + emit mCompressionLevelProperty->valueChanged(); + break; case Map::ChunkSizeProperty: + emit mChunkSizeProperty->valueChanged(); + break; + } + + updateEnabledState(); + } + + void updateEnabledState() + { + const auto orientation = map()->orientation(); + const bool stagger = orientation == Map::Staggered || orientation == Map::Hexagonal; + + mHexSideLengthProperty->setEnabled(orientation == Map::Hexagonal); + mStaggerAxisProperty->setEnabled(stagger); + mStaggerIndexProperty->setEnabled(stagger); + mRenderOrderProperty->setEnabled(orientation == Map::Orthogonal); + mChunkSizeProperty->setEnabled(map()->infinite()); + + switch (map()->layerDataFormat()) { + case Map::XML: + case Map::Base64: + case Map::CSV: + mCompressionLevelProperty->setEnabled(false); + break; + case Map::Base64Gzip: + case Map::Base64Zlib: + case Map::Base64Zstandard: + mCompressionLevelProperty->setEnabled(true); break; } } + void push(QUndoCommand *command) + { + mMapDocument->undoStack()->push(command); + } + + Map *map() const + { + return mMapDocument->map(); + } + MapDocument *mMapDocument; + Property *mClassProperty; Property *mOrientationProperty; Property *mSizeProperty; Property *mTileSizeProperty; @@ -399,7 +484,7 @@ class MapProperties : public QObject Property *mParallaxOriginProperty; Property *mLayerDataFormatProperty; Property *mChunkSizeProperty; - Property *mTileRenderOrderProperty; + Property *mRenderOrderProperty; Property *mCompressionLevelProperty; Property *mBackgroundColorProperty; }; @@ -831,6 +916,21 @@ void PropertiesWidget::registerEditorFactories() tr("Bottom Right"), })); + // We leave out the "Unknown" orientation, because it shouldn't occur here + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Orthogonal"), + tr("Isometric"), + tr("Isometric (Staggered)"), + tr("Hexagonal (Staggered)"), + }, + QList { + Map::Orthogonal, + Map::Isometric, + Map::Staggered, + Map::Hexagonal, + })); registerEditorFactory(qMetaTypeId(), std::make_unique( diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 081f3f8f95..f1eebbd365 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -21,8 +21,6 @@ #include "varianteditor.h" #include "colorbutton.h" -#include "compression.h" -#include "map.h" #include "utils.h" #include "propertyeditorwidgets.h" @@ -71,9 +69,15 @@ class StringEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new QLineEdit(parent); - editor->setText(value.toString()); + auto syncEditor = [=] { + editor->setText(property->value().toString()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &QLineEdit::textEdited, property, &Property::setValue); + return editor; } }; @@ -83,9 +87,17 @@ class IntEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new SpinBox(parent); - editor->setValue(value.toInt()); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toInt()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&SpinBox::valueChanged), + property, &Property::setValue); + return editor; } }; @@ -95,9 +107,17 @@ class FloatEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new DoubleSpinBox(parent); - editor->setValue(value.toDouble()); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toDouble()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), + property, &Property::setValue); + return editor; } }; @@ -108,7 +128,7 @@ class BoolEditorFactory : public EditorFactory QWidget *createEditor(Property *property, QWidget *parent) override { auto editor = new QCheckBox(parent); - auto syncEditor = [=]() { + auto syncEditor = [=] { const QSignalBlocker blocker(editor); bool checked = property->value().toBool(); editor->setChecked(checked); @@ -131,7 +151,6 @@ class PointEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new QWidget(parent); auto horizontalLayout = new QHBoxLayout(editor); horizontalLayout->setContentsMargins(QMargins()); @@ -150,8 +169,23 @@ class PointEditorFactory : public EditorFactory yLabel->setBuddy(ySpinBox); horizontalLayout->addWidget(ySpinBox, 1); - xSpinBox->setValue(value.toPoint().x()); - ySpinBox->setValue(value.toPoint().y()); + auto syncEditor = [=] { + const QSignalBlocker xBlocker(xSpinBox); + const QSignalBlocker yBlocker(ySpinBox); + const auto point = property->value().toPoint(); + xSpinBox->setValue(point.x()); + ySpinBox->setValue(point.y()); + }; + auto syncProperty = [=] { + property->setValue(QPoint(xSpinBox->value(), ySpinBox->value())); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(xSpinBox, qOverload(&SpinBox::valueChanged), + property, syncProperty); + QObject::connect(ySpinBox, qOverload(&SpinBox::valueChanged), + property, syncProperty); return editor; } @@ -162,7 +196,6 @@ class PointFEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new QWidget(parent); auto horizontalLayout = new QHBoxLayout(editor); horizontalLayout->setContentsMargins(QMargins()); @@ -181,8 +214,23 @@ class PointFEditorFactory : public EditorFactory yLabel->setBuddy(ySpinBox); horizontalLayout->addWidget(ySpinBox, 1); - xSpinBox->setValue(value.toPointF().x()); - ySpinBox->setValue(value.toPointF().y()); + auto syncEditor = [=] { + const QSignalBlocker xBlocker(xSpinBox); + const QSignalBlocker yBlocker(ySpinBox); + const auto point = property->value().toPointF(); + xSpinBox->setValue(point.x()); + ySpinBox->setValue(point.y()); + }; + auto syncProperty = [=] { + property->setValue(QPointF(xSpinBox->value(), ySpinBox->value())); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(xSpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); + QObject::connect(ySpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); return editor; } @@ -195,7 +243,7 @@ class SizeEditorFactory : public EditorFactory QWidget *createEditor(Property *property, QWidget *parent) override { auto editor = new SizeEdit(parent); - auto syncEditor = [property, editor]() { + auto syncEditor = [property, editor] { const QSignalBlocker blocker(editor); editor->setValue(property->value().toSize()); }; @@ -203,7 +251,7 @@ class SizeEditorFactory : public EditorFactory QObject::connect(property, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &SizeEdit::valueChanged, property, - [property, editor]() { + [property, editor] { property->setValue(editor->value()); }); @@ -216,7 +264,6 @@ class RectFEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new QWidget(parent); auto gridLayout = new QGridLayout(editor); gridLayout->setContentsMargins(QMargins()); @@ -252,11 +299,34 @@ class RectFEditorFactory : public EditorFactory heightLabel->setBuddy(heightSpinBox); gridLayout->addWidget(heightSpinBox, 1, 3); - const auto rect = value.toRectF(); - xSpinBox->setValue(rect.x()); - ySpinBox->setValue(rect.y()); - widthSpinBox->setValue(rect.width()); - heightSpinBox->setValue(rect.height()); + auto syncEditor = [=] { + const QSignalBlocker xBlocker(xSpinBox); + const QSignalBlocker yBlocker(ySpinBox); + const QSignalBlocker widthBlocker(widthSpinBox); + const QSignalBlocker heightBlocker(heightSpinBox); + const auto rect = property->value().toRectF(); + xSpinBox->setValue(rect.x()); + ySpinBox->setValue(rect.y()); + widthSpinBox->setValue(rect.width()); + heightSpinBox->setValue(rect.height()); + }; + auto syncProperty = [=] { + property->setValue(QRectF(xSpinBox->value(), + ySpinBox->value(), + widthSpinBox->value(), + heightSpinBox->value())); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(xSpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); + QObject::connect(ySpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); + QObject::connect(widthSpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); + QObject::connect(heightSpinBox, qOverload(&DoubleSpinBox::valueChanged), + property, syncProperty); return editor; } @@ -267,9 +337,19 @@ class ColorEditorFactory : public EditorFactory public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto value = property->value(); auto editor = new ColorButton(parent); - editor->setColor(value.value()); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setColor(property->value().value()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &ColorButton::colorChanged, property, + [property, editor] { + property->setValue(editor->color()); + }); + return editor; } }; @@ -348,29 +428,6 @@ VariantEditor::VariantEditor(QWidget *parent) // QVariant(10), // QVariant(3.14) // }); - - // addHeader(tr("Map")); - // addProperty(new VariantProperty(tr("Class"), QString())); - // addProperty(new VariantProperty(tr("Orientation"), QVariant::fromValue(Map::Hexagonal))); - // addValue(tr("Class"), QString()); - // addSeparator(); - // addValue(tr("Orientation"), QVariant::fromValue(Map::Hexagonal)); - // addValue(tr("Infinite"), false); - // addValue(tr("Map Size"), QSize(20, 20)); - // addValue(tr("Tile Size"), QSize(14, 12)); - // addValue(tr("Tile Side Length (Hex)"), 6); - // addValue(tr("Stagger Axis"), QVariant::fromValue(Map::StaggerY)); - // addValue(tr("Stagger Index"), QVariant::fromValue(Map::StaggerEven)); - // addSeparator(); - // addValue(tr("Parallax Origin"), QPointF()); - // addSeparator(); - // addValue(tr("Tile Layer Format"), QVariant::fromValue(Map::Base64Zlib)); - // addValue(tr("Output Chunk Size"), QSize(16, 16)); - // addValue(tr("Compression Level"), -1); - // addSeparator(); - // addValue(tr("Tile Render Order"), QVariant::fromValue(Map::RightDown)); - // addValue(tr("Background Color"), QColor()); - // addHeader(tr("Custom Properties")); } void VariantEditor::clear() @@ -489,7 +546,7 @@ QWidget *EnumEditorFactory::createEditor(Property *property, QWidget *parent) editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); editor->setModel(&m_enumNamesModel); - auto syncEditor = [property, editor, this]() { + auto syncEditor = [property, editor, this] { const QSignalBlocker blocker(editor); if (m_enumValues.isEmpty()) editor->setCurrentIndex(property->value().toInt()); From 5e9cb22077d273e9c54e8395680f56db78f76947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 4 Sep 2024 10:47:39 +0200 Subject: [PATCH 08/78] Implemented custom widget for Class property This property uses an editable combo box with the valid classes set up for the given object type. --- src/tiled/changeproperties.cpp | 2 +- src/tiled/propertieswidget.cpp | 83 ++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/tiled/changeproperties.cpp b/src/tiled/changeproperties.cpp index 613c3022ec..9b36a3a479 100644 --- a/src/tiled/changeproperties.cpp +++ b/src/tiled/changeproperties.cpp @@ -34,7 +34,7 @@ ChangeClassName::ChangeClassName(Document *document, QUndoCommand *parent) : ChangeValue(document, objects, className, parent) { - setText(QCoreApplication::translate("Undo Commands", "Change Type")); + setText(QCoreApplication::translate("Undo Commands", "Change Class")); } void ChangeClassName::undo() diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 21e1ff3f46..5dd9d18db4 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -27,11 +27,13 @@ #include "clipboardmanager.h" #include "compression.h" #include "mapdocument.h" +#include "preferences.h" #include "propertybrowser.h" #include "utils.h" #include "varianteditor.h" #include +#include #include #include #include @@ -146,6 +148,77 @@ static bool anyObjectHasProperty(const QList &objects, const QString &n return false; } +static QStringList classNamesFor(const Object &object) +{ + QStringList names; + for (const auto type : Object::propertyTypes()) + if (type->isClass()) + if (static_cast(type)->isClassFor(object)) + names.append(type->name); + return names; +} + +class ClassProperty : public Property +{ + Q_OBJECT + +public: + ClassProperty(Document *document, Object *object, QObject *parent = nullptr) + : Property(tr("Class"), parent) + , mDocument(document) + , mObject(object) + { + connect(mDocument, &Document::changed, + this, &ClassProperty::onChanged); + } + + QVariant value() const override { return mObject->className(); } + void setValue(const QVariant &value) override + { + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->push(new ChangeClassName(mDocument, + { mObject }, // todo: add support for changing multiple objects + value.toString())); + } + + QWidget *createEditor(QWidget *parent) override + { + auto editor = new QComboBox(parent); + editor->setEditable(true); + editor->addItems(classNamesFor(*mObject)); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setCurrentText(value().toString()); + }; + syncEditor(); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &QComboBox::currentTextChanged, this, &Property::setValue); + connect(Preferences::instance(), &Preferences::propertyTypesChanged, + editor, [this,editor] { + editor->clear(); + editor->addItems(classNamesFor(*mObject)); + }); + return editor; + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::ObjectsChanged) + return; + + const auto objectsEvent = static_cast(event); + if (!objectsEvent.objects.contains(mObject)) + return; + + if (objectsEvent.properties & ObjectsChangeEvent::ClassProperty) + emit valueChanged(); + } + + Document *mDocument; + Object *mObject; +}; + class MapSizeProperty : public AbstractProperty { Q_OBJECT @@ -241,15 +314,7 @@ class MapProperties : public QObject , mSizeProperty(new MapSizeProperty(mapDocument, editorFactory, this)) , mTileSizeProperty(new TileSizeProperty(mapDocument, editorFactory, this)) { - mClassProperty = editorFactory->createProperty( - tr("Class"), - [this]() { - return map()->className(); - }, - [this](const QVariant &value) { - push(new ChangeClassName(mMapDocument, { map() }, - value.toString())); - }); + mClassProperty = new ClassProperty(mMapDocument, mMapDocument->map()); mOrientationProperty = editorFactory->createProperty( tr("Orientation"), From ad0a4dd0285435c329ea8f85969656e2e0188edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 4 Sep 2024 12:24:44 +0200 Subject: [PATCH 09/78] Made most Tileset properties functional Still needs special handling for: * Color (requires handling invalid/unset values) * Widget for editing allowed transformations * Widget for showing tileset parameters (and trigger edit dialog) * Specifying 1 as minimum value for column count --- src/tiled/propertieswidget.cpp | 250 ++++++++++++++++++++++++++++++++- src/tiled/varianteditor.cpp | 1 + 2 files changed, 246 insertions(+), 5 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 5dd9d18db4..1dd8bdfa45 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -29,6 +29,8 @@ #include "mapdocument.h" #include "preferences.h" #include "propertybrowser.h" +#include "tilesetchanges.h" +#include "tilesetdocument.h" #include "utils.h" #include "varianteditor.h" @@ -425,8 +427,8 @@ class MapProperties : public QObject }); updateEnabledState(); - connect(mMapDocument, &MapDocument::changed, - this, &MapProperties::onMapChanged); + connect(mMapDocument, &Document::changed, + this, &MapProperties::onChanged); } void populateEditor(VariantEditor *editor) @@ -453,7 +455,7 @@ class MapProperties : public QObject } private: - void onMapChanged(const ChangeEvent &event) + void onChanged(const ChangeEvent &event) { if (event.type != ChangeEvent::MapChanged) return; @@ -554,10 +556,221 @@ class MapProperties : public QObject Property *mBackgroundColorProperty; }; +class TilesetProperties : public QObject +{ + Q_OBJECT + +public: + TilesetProperties(TilesetDocument *tilesetDocument, + ValueTypeEditorFactory *editorFactory, + QObject *parent = nullptr) + : QObject(parent) + , mTilesetDocument(tilesetDocument) + { + mNameProperty = editorFactory->createProperty( + tr("Name"), + [this]() { + return mTilesetDocument->tileset()->name(); + }, + [this](const QVariant &value) { + push(new RenameTileset(mTilesetDocument, value.toString())); + }); + + mClassProperty = new ClassProperty(tilesetDocument, tilesetDocument->tileset().data()); + + mObjectAlignmentProperty = editorFactory->createProperty( + tr("Object Alignment"), + [this]() { + return QVariant::fromValue(tileset()->objectAlignment()); + }, + [this](const QVariant &value) { + const auto objectAlignment = static_cast(value.toInt()); + push(new ChangeTilesetObjectAlignment(mTilesetDocument, objectAlignment)); + }); + + mTileOffsetProperty = editorFactory->createProperty( + tr("Drawing Offset"), + [this]() { + return tileset()->tileOffset(); + }, + [this](const QVariant &value) { + push(new ChangeTilesetTileOffset(mTilesetDocument, value.value())); + }); + + mTileRenderSizeProperty = editorFactory->createProperty( + tr("Tile Render Size"), + [this]() { + return QVariant::fromValue(tileset()->tileRenderSize()); + }, + [this](const QVariant &value) { + const auto tileRenderSize = static_cast(value.toInt()); + push(new ChangeTilesetTileRenderSize(mTilesetDocument, tileRenderSize)); + }); + + mFillModeProperty = editorFactory->createProperty( + tr("Fill Mode"), + [this]() { + return QVariant::fromValue(tileset()->fillMode()); + }, + [this](const QVariant &value) { + const auto fillMode = static_cast(value.toInt()); + push(new ChangeTilesetFillMode(mTilesetDocument, fillMode)); + }); + + mBackgroundColorProperty = editorFactory->createProperty( + tr("Background Color"), + [this]() { + return tileset()->backgroundColor(); + }, + [this](const QVariant &value) { + push(new ChangeTilesetBackgroundColor(mTilesetDocument, value.value())); + }); + + mOrientationProperty = editorFactory->createProperty( + tr("Orientation"), + [this]() { + return QVariant::fromValue(tileset()->orientation()); + }, + [this](const QVariant &value) { + const auto orientation = static_cast(value.toInt()); + push(new ChangeTilesetOrientation(mTilesetDocument, orientation)); + }); + + mGridSizeProperty = editorFactory->createProperty( + tr("Grid Size"), + [this]() { + return tileset()->gridSize(); + }, + [this](const QVariant &value) { + push(new ChangeTilesetGridSize(mTilesetDocument, value.toSize())); + }); + + // todo: needs 1 as minimum value + mColumnCountProperty = editorFactory->createProperty( + tr("Columns"), + [this]() { + return tileset()->columnCount(); + }, + [this](const QVariant &value) { + push(new ChangeTilesetColumnCount(mTilesetDocument, value.toInt())); + }); + + // todo: this needs a custom widget + mAllowedTransformationsProperty = editorFactory->createProperty( + tr("Allowed Transformations"), + [this]() { + return QVariant::fromValue(tileset()->transformationFlags()); + }, + [this](const QVariant &value) { + const auto flags = static_cast(value.toInt()); + push(new ChangeTilesetTransformationFlags(mTilesetDocument, flags)); + }); + + // todo: this needs a custom widget + mImageProperty = editorFactory->createProperty( + tr("Image"), + [this]() { + return tileset()->imageSource().toString(); + }, + [](const QVariant &) { + // push(new ChangeTilesetImage(mTilesetDocument, value.toString())); + }); + + updateEnabledState(); + connect(mTilesetDocument, &Document::changed, + this, &TilesetProperties::onChanged); + + connect(mTilesetDocument, &TilesetDocument::tilesetNameChanged, + mNameProperty, &Property::valueChanged); + connect(mTilesetDocument, &TilesetDocument::tilesetTileOffsetChanged, + mTileOffsetProperty, &Property::valueChanged); + connect(mTilesetDocument, &TilesetDocument::tilesetObjectAlignmentChanged, + mObjectAlignmentProperty, &Property::valueChanged); + connect(mTilesetDocument, &TilesetDocument::tilesetChanged, + this, &TilesetProperties::onTilesetChanged); + } + + void populateEditor(VariantEditor *editor) + { + editor->addHeader(tr("Tileset")); + editor->addProperty(mNameProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + editor->addProperty(mObjectAlignmentProperty); + editor->addProperty(mTileOffsetProperty); + editor->addProperty(mTileRenderSizeProperty); + editor->addProperty(mFillModeProperty); + editor->addProperty(mBackgroundColorProperty); + editor->addProperty(mOrientationProperty); + editor->addProperty(mGridSizeProperty); + editor->addProperty(mColumnCountProperty); + editor->addProperty(mAllowedTransformationsProperty); + editor->addProperty(mImageProperty); + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::TilesetChanged) + return; + + const auto property = static_cast(event).property; + switch (property) { + case Tileset::FillModeProperty: + emit mFillModeProperty->valueChanged(); + break; + case Tileset::TileRenderSizeProperty: + emit mTileRenderSizeProperty->valueChanged(); + break; + } + } + + void onTilesetChanged(Tileset *) + { + // the following properties have no specific change events + emit mBackgroundColorProperty->valueChanged(); + emit mOrientationProperty->valueChanged(); + emit mGridSizeProperty->valueChanged(); + emit mColumnCountProperty->valueChanged(); + emit mAllowedTransformationsProperty->valueChanged(); + emit mImageProperty->valueChanged(); + } + + void updateEnabledState() + { + const bool collection = tileset()->isCollection(); + mImageProperty->setEnabled(!collection); + mColumnCountProperty->setEnabled(collection); + } + + void push(QUndoCommand *command) + { + mTilesetDocument->undoStack()->push(command); + } + + Tileset *tileset() const + { + return mTilesetDocument->tileset().data(); + } + + TilesetDocument *mTilesetDocument; + Property *mNameProperty; + Property *mClassProperty; + Property *mObjectAlignmentProperty; + Property *mTileOffsetProperty; + Property *mTileRenderSizeProperty; + Property *mFillModeProperty; + Property *mBackgroundColorProperty; + Property *mOrientationProperty; + Property *mGridSizeProperty; + Property *mColumnCountProperty; + Property *mAllowedTransformationsProperty; + Property *mImageProperty; +}; + void PropertiesWidget::currentObjectChanged(Object *object) { - // mPropertyBrowser->setObject(object); mPropertyBrowser->clear(); delete mPropertiesObject; mPropertiesObject = nullptr; @@ -574,7 +787,13 @@ void PropertiesWidget::currentObjectChanged(Object *object) mPropertiesObject = properties; break; } - case Object::TilesetType: + case Object::TilesetType: { + auto tilesetDocument = static_cast(mDocument); + auto properties = new TilesetProperties(tilesetDocument, + mDefaultEditorFactory.get(), this); + properties->populateEditor(mPropertyBrowser); + mPropertiesObject = properties; + } case Object::TileType: case Object::WangSetType: case Object::WangColorType: @@ -1043,6 +1262,27 @@ void PropertiesWidget::registerEditorFactories() tr("Left Down"), tr("Left Up"), })); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Orthogonal"), + tr("Isometric"), + })); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Tile Size"), + tr("Map Grid Size"), + })); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Stretch"), + tr("Preserve Aspect Ratio"), + })); } void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index f1eebbd365..b266051cca 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -332,6 +332,7 @@ class RectFEditorFactory : public EditorFactory } }; +// todo: needs to handle invalid color (unset value) class ColorEditorFactory : public EditorFactory { public: From 754fd17f322e46c3d1fcf270feb7ed4ba8e602f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 4 Sep 2024 13:08:05 +0200 Subject: [PATCH 10/78] Implemented Layer properties Some details remain to be done: * Changes made to layer properties should apply to all selected layers * Opacity should probably be a slider now and have appropriate limits * Step size for opacity and parallax factor is too big --- src/tiled/propertieswidget.cpp | 141 ++++++++++++++++++++++++++++++++- src/tiled/varianteditor.cpp | 3 + 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 1dd8bdfa45..740da361a8 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -22,6 +22,7 @@ #include "actionmanager.h" #include "addpropertydialog.h" +#include "changelayer.h" #include "changemapproperty.h" #include "changeproperties.h" #include "clipboardmanager.h" @@ -556,6 +557,138 @@ class MapProperties : public QObject Property *mBackgroundColorProperty; }; +class LayerProperties : public QObject +{ + Q_OBJECT + +public: + LayerProperties(MapDocument *mapDocument, Layer *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : QObject(parent) + , mMapDocument(mapDocument) + , mLayer(layer) + { + // todo: would be nicer to avoid the SpinBox and use a custom widget + mIdProperty = editorFactory->createProperty( + tr("ID"), + [this]() { return mLayer->id(); }, + [](const QVariant &) {}); + mIdProperty->setEnabled(false); + + // todo: the below should be able to apply to all selected layers + + mNameProperty = editorFactory->createProperty( + tr("Name"), + [this]() { return mLayer->name(); }, + [this](const QVariant &value) { + push(new SetLayerName(mMapDocument, { mLayer }, value.toString())); + }); + + mClassProperty = new ClassProperty(mMapDocument, mLayer); + + mVisibleProperty = editorFactory->createProperty( + tr("Visible"), + [this]() { return mLayer->isVisible(); }, + [this](const QVariant &value) { + push(new SetLayerVisible(mMapDocument, { mLayer }, value.toBool())); + }); + + mLockedProperty = editorFactory->createProperty( + tr("Locked"), + [this]() { return mLayer->isLocked(); }, + [this](const QVariant &value) { + push(new SetLayerLocked(mMapDocument, { mLayer }, value.toBool())); + }); + + // todo: value should be between 0 and 1, and would be nice to use a slider (replacing the one in Layers view) + // todo: singleStep should be 0.1 + mOpacityProperty = editorFactory->createProperty( + tr("Opacity"), + [this]() { return mLayer->opacity(); }, + [this](const QVariant &value) { + push(new SetLayerOpacity(mMapDocument, { mLayer }, value.toReal())); + }); + + mTintColorProperty = editorFactory->createProperty( + tr("Tint Color"), + [this]() { return mLayer->tintColor(); }, + [this](const QVariant &value) { + push(new SetLayerTintColor(mMapDocument, { mLayer }, value.value())); + }); + + mOffsetProperty = editorFactory->createProperty( + tr("Offset"), + [this]() { return mLayer->offset(); }, + [this](const QVariant &value) { + push(new SetLayerOffset(mMapDocument, { mLayer }, value.value())); + }); + + // todo: singleStep should be 0.1 + mParallaxFactorProperty = editorFactory->createProperty( + tr("Parallax Factor"), + [this]() { return mLayer->parallaxFactor(); }, + [this](const QVariant &value) { + push(new SetLayerParallaxFactor(mMapDocument, { mLayer }, value.toPointF())); + }); + + connect(mMapDocument, &Document::changed, + this, &LayerProperties::onChanged); + } + + void populateEditor(VariantEditor *editor) + { + editor->addHeader(tr("Layer")); + editor->addProperty(mIdProperty); + editor->addProperty(mNameProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + editor->addProperty(mVisibleProperty); + editor->addProperty(mLockedProperty); + editor->addProperty(mOpacityProperty); + editor->addProperty(mTintColorProperty); + editor->addProperty(mOffsetProperty); + editor->addProperty(mParallaxFactorProperty); + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::LayerChanged) + return; + + const auto properties = static_cast(event).properties; + if (properties & LayerChangeEvent::VisibleProperty) + emit mVisibleProperty->valueChanged(); + if (properties & LayerChangeEvent::LockedProperty) + emit mLockedProperty->valueChanged(); + if (properties & LayerChangeEvent::OpacityProperty) + emit mOpacityProperty->valueChanged(); + if (properties & LayerChangeEvent::TintColorProperty) + emit mTintColorProperty->valueChanged(); + if (properties & LayerChangeEvent::OffsetProperty) + emit mOffsetProperty->valueChanged(); + if (properties & LayerChangeEvent::ParallaxFactorProperty) + emit mParallaxFactorProperty->valueChanged(); + } + + void push(QUndoCommand *command) + { + mMapDocument->undoStack()->push(command); + } + + MapDocument *mMapDocument; + Layer *mLayer; + + Property *mIdProperty; + Property *mNameProperty; + Property *mClassProperty; + Property *mVisibleProperty; + Property *mLockedProperty; + Property *mOpacityProperty; + Property *mTintColorProperty; + Property *mOffsetProperty; + Property *mParallaxFactorProperty; +}; + class TilesetProperties : public QObject { Q_OBJECT @@ -777,7 +910,13 @@ void PropertiesWidget::currentObjectChanged(Object *object) if (object) { switch (object->typeId()) { - case Object::LayerType: + case Object::LayerType: { + auto mapDocument = static_cast(mDocument); + auto layer = static_cast(object); + auto properties = new LayerProperties(mapDocument, layer, mDefaultEditorFactory.get(), this); + properties->populateEditor(mPropertyBrowser); + mPropertiesObject = properties; + } case Object::MapObjectType: break; case Object::MapType: { diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index b266051cca..7037a6337a 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -146,6 +146,7 @@ class BoolEditorFactory : public EditorFactory } }; +// todo: implement responsive layout (see SizeEdit) class PointEditorFactory : public EditorFactory { public: @@ -191,6 +192,7 @@ class PointEditorFactory : public EditorFactory } }; +// todo: implement responsive layout (see SizeEdit) class PointFEditorFactory : public EditorFactory { public: @@ -259,6 +261,7 @@ class SizeEditorFactory : public EditorFactory } }; +// todo: implement responsive layout (see SizeEdit) class RectFEditorFactory : public EditorFactory { public: From ba5c31d8be6e0f50b0272f739b3c21111dbda909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 4 Sep 2024 14:02:06 +0200 Subject: [PATCH 11/78] Added Image Layer and Group Layer properties Added support for editing QUrl values using UrlEditorFactory that creates a FileEdit. --- src/libtiled/objectgroup.h | 1 + src/libtiled/tileset.h | 4 + src/tiled/changeevents.h | 2 +- src/tiled/propertieswidget.cpp | 202 ++++++++++++++++++++++++++++++--- src/tiled/varianteditor.cpp | 30 +++++ 5 files changed, 225 insertions(+), 14 deletions(-) diff --git a/src/libtiled/objectgroup.h b/src/libtiled/objectgroup.h index 07765ebdbd..3abacd4a7e 100644 --- a/src/libtiled/objectgroup.h +++ b/src/libtiled/objectgroup.h @@ -238,3 +238,4 @@ TILEDSHARED_EXPORT ObjectGroup::DrawOrder drawOrderFromString(const QString &); } // namespace Tiled Q_DECLARE_METATYPE(Tiled::ObjectGroup*) +Q_DECLARE_METATYPE(Tiled::ObjectGroup::DrawOrder) diff --git a/src/libtiled/tileset.h b/src/libtiled/tileset.h index c7b5b01156..934adfdbfe 100644 --- a/src/libtiled/tileset.h +++ b/src/libtiled/tileset.h @@ -739,5 +739,9 @@ inline void Tileset::setTransformationFlags(TransformationFlags flags) Q_DECLARE_METATYPE(Tiled::Tileset*) Q_DECLARE_METATYPE(Tiled::SharedTileset) +Q_DECLARE_METATYPE(Tiled::Tileset::Orientation) +Q_DECLARE_METATYPE(Tiled::Tileset::TileRenderSize) +Q_DECLARE_METATYPE(Tiled::Tileset::FillMode) +Q_DECLARE_METATYPE(Tiled::Tileset::TransformationFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::Tileset::TransformationFlags) diff --git a/src/tiled/changeevents.h b/src/tiled/changeevents.h index c5e4061f27..0dbf599280 100644 --- a/src/tiled/changeevents.h +++ b/src/tiled/changeevents.h @@ -164,7 +164,7 @@ class TileLayerChangeEvent : public LayerChangeEvent class ImageLayerChangeEvent : public LayerChangeEvent { public: - enum TileLayerProperty { + enum ImageLayerProperty { TransparentColorProperty = 1 << 7, ImageSourceProperty = 1 << 8, RepeatProperty = 1 << 9, diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 740da361a8..c7d4cc67f0 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -22,12 +22,15 @@ #include "actionmanager.h" #include "addpropertydialog.h" +#include "changeimagelayerproperty.h" #include "changelayer.h" #include "changemapproperty.h" +#include "changeobjectgroupproperties.h" #include "changeproperties.h" #include "clipboardmanager.h" #include "compression.h" #include "mapdocument.h" +#include "objectgroup.h" #include "preferences.h" #include "propertybrowser.h" #include "tilesetchanges.h" @@ -568,6 +571,7 @@ class LayerProperties : public QObject , mLayer(layer) { // todo: would be nicer to avoid the SpinBox and use a custom widget + // might also be nice to embed this in the header instead of using a property mIdProperty = editorFactory->createProperty( tr("ID"), [this]() { return mLayer->id(); }, @@ -634,7 +638,7 @@ class LayerProperties : public QObject this, &LayerProperties::onChanged); } - void populateEditor(VariantEditor *editor) + virtual void populateEditor(VariantEditor *editor) { editor->addHeader(tr("Layer")); editor->addProperty(mIdProperty); @@ -649,24 +653,27 @@ class LayerProperties : public QObject editor->addProperty(mParallaxFactorProperty); } -private: - void onChanged(const ChangeEvent &event) +protected: + virtual void onChanged(const ChangeEvent &event) { if (event.type != ChangeEvent::LayerChanged) return; - const auto properties = static_cast(event).properties; - if (properties & LayerChangeEvent::VisibleProperty) + const auto &layerChange = static_cast(event); + if (layerChange.layer != mLayer) + return; + + if (layerChange.properties & LayerChangeEvent::VisibleProperty) emit mVisibleProperty->valueChanged(); - if (properties & LayerChangeEvent::LockedProperty) + if (layerChange.properties & LayerChangeEvent::LockedProperty) emit mLockedProperty->valueChanged(); - if (properties & LayerChangeEvent::OpacityProperty) + if (layerChange.properties & LayerChangeEvent::OpacityProperty) emit mOpacityProperty->valueChanged(); - if (properties & LayerChangeEvent::TintColorProperty) + if (layerChange.properties & LayerChangeEvent::TintColorProperty) emit mTintColorProperty->valueChanged(); - if (properties & LayerChangeEvent::OffsetProperty) + if (layerChange.properties & LayerChangeEvent::OffsetProperty) emit mOffsetProperty->valueChanged(); - if (properties & LayerChangeEvent::ParallaxFactorProperty) + if (layerChange.properties & LayerChangeEvent::ParallaxFactorProperty) emit mParallaxFactorProperty->valueChanged(); } @@ -689,6 +696,148 @@ class LayerProperties : public QObject Property *mParallaxFactorProperty; }; +class ImageLayerProperties : public LayerProperties +{ + Q_OBJECT + +public: + ImageLayerProperties(MapDocument *mapDocument, ImageLayer *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : LayerProperties(mapDocument, layer, editorFactory, parent) + { + // todo: set a file filter for selecting images (or map files?) + mImageProperty = editorFactory->createProperty( + tr("Image Source"), + [this]() { return imageLayer()->imageSource(); }, + [this](const QVariant &value) { + push(new ChangeImageLayerImageSource(mMapDocument, { imageLayer() }, value.toUrl())); + }); + + mTransparentColorProperty = editorFactory->createProperty( + tr("Transparent Color"), + [this]() { return imageLayer()->transparentColor(); }, + [this](const QVariant &value) { + push(new ChangeImageLayerTransparentColor(mMapDocument, { imageLayer() }, value.value())); + }); + + // todo: consider merging Repeat X and Y into a single property + mRepeatXProperty = editorFactory->createProperty( + tr("Repeat X"), + [this]() { return imageLayer()->repeatX(); }, + [this](const QVariant &value) { + push(new ChangeImageLayerRepeatX(mMapDocument, { imageLayer() }, value.toBool())); + }); + + mRepeatYProperty = editorFactory->createProperty( + tr("Repeat Y"), + [this]() { return imageLayer()->repeatY(); }, + [this](const QVariant &value) { + push(new ChangeImageLayerRepeatY(mMapDocument, { imageLayer() }, value.toBool())); + }); + } + + void populateEditor(VariantEditor *editor) override + { + LayerProperties::populateEditor(editor); + editor->addHeader(tr("Image Layer")); + editor->addProperty(mImageProperty); + editor->addProperty(mTransparentColorProperty); + editor->addSeparator(); + editor->addProperty(mRepeatXProperty); + editor->addProperty(mRepeatYProperty); + } + +private: + void onChanged(const ChangeEvent &event) override + { + LayerProperties::onChanged(event); + + if (event.type != ChangeEvent::ImageLayerChanged) + return; + + const auto &layerChange = static_cast(event); + if (layerChange.layer != mLayer) + return; + + if (layerChange.properties & ImageLayerChangeEvent::ImageSourceProperty) + emit mImageProperty->valueChanged(); + if (layerChange.properties & ImageLayerChangeEvent::TransparentColorProperty) + emit mTransparentColorProperty->valueChanged(); + if (layerChange.properties & ImageLayerChangeEvent::RepeatProperty) { + emit mRepeatXProperty->valueChanged(); + emit mRepeatYProperty->valueChanged(); + } + } + + ImageLayer *imageLayer() const + { + return static_cast(mLayer); + } + + Property *mImageProperty; + Property *mTransparentColorProperty; + Property *mRepeatXProperty; + Property *mRepeatYProperty; +}; + +class ObjectGroupProperties : public LayerProperties +{ + Q_OBJECT + +public: + ObjectGroupProperties(MapDocument *mapDocument, ObjectGroup *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : LayerProperties(mapDocument, layer, editorFactory, parent) + { + mColorProperty = editorFactory->createProperty( + tr("Color"), + [this]() { return objectGroup()->color(); }, + [this](const QVariant &value) { + push(new ChangeObjectGroupColor(mMapDocument, { objectGroup() }, value.value())); + }); + + mDrawOrderProperty = editorFactory->createProperty( + tr("Draw Order"), + [this]() { return QVariant::fromValue(objectGroup()->drawOrder()); }, + [this](const QVariant &value) { + ObjectGroup::DrawOrder drawOrder = static_cast(value.toInt()); + push(new ChangeObjectGroupDrawOrder(mMapDocument, { objectGroup() }, drawOrder)); + }); + } + + void populateEditor(VariantEditor *editor) override + { + LayerProperties::populateEditor(editor); + editor->addHeader(tr("Object Layer")); + editor->addProperty(mColorProperty); + editor->addProperty(mDrawOrderProperty); + } + +private: + void onChanged(const ChangeEvent &event) override + { + LayerProperties::onChanged(event); + + if (event.type != ChangeEvent::ObjectGroupChanged) + return; + + const auto &layerChange = static_cast(event); + if (layerChange.objectGroup != objectGroup()) + return; + + if (layerChange.properties & ObjectGroupChangeEvent::ColorProperty) + emit mColorProperty->valueChanged(); + if (layerChange.properties & ObjectGroupChangeEvent::DrawOrderProperty) + emit mDrawOrderProperty->valueChanged(); + } + + ObjectGroup *objectGroup() const + { + return static_cast(mLayer); + } + + Property *mColorProperty; + Property *mDrawOrderProperty; +}; + class TilesetProperties : public QObject { Q_OBJECT @@ -913,9 +1062,29 @@ void PropertiesWidget::currentObjectChanged(Object *object) case Object::LayerType: { auto mapDocument = static_cast(mDocument); auto layer = static_cast(object); - auto properties = new LayerProperties(mapDocument, layer, mDefaultEditorFactory.get(), this); - properties->populateEditor(mPropertyBrowser); - mPropertiesObject = properties; + LayerProperties *layerProperties = nullptr; + + switch (layer->layerType()) { + case Layer::ImageLayerType: + layerProperties = new ImageLayerProperties(mapDocument, + static_cast(layer), + mDefaultEditorFactory.get(), this); + break; + case Layer::ObjectGroupType: + layerProperties = new ObjectGroupProperties(mapDocument, + static_cast(layer), + mDefaultEditorFactory.get(), this); + break; + case Layer::TileLayerType: + case Layer::GroupLayerType: + layerProperties = new LayerProperties(mapDocument, + layer, + mDefaultEditorFactory.get(), this); + break; + } + + layerProperties->populateEditor(mPropertyBrowser); + mPropertiesObject = layerProperties; } case Object::MapObjectType: break; @@ -1422,6 +1591,13 @@ void PropertiesWidget::registerEditorFactories() tr("Stretch"), tr("Preserve Aspect Ratio"), })); + + registerEditorFactory(qMetaTypeId(), + std::make_unique( + QStringList { + tr("Top Down"), + tr("Index Order"), + })); } void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 7037a6337a..854b52d5c7 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -21,6 +21,7 @@ #include "varianteditor.h" #include "colorbutton.h" +#include "fileedit.h" #include "utils.h" #include "propertyeditorwidgets.h" @@ -82,6 +83,34 @@ class StringEditorFactory : public EditorFactory } }; +class UrlEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(Property *property, QWidget *parent) override + { + auto editor = new FileEdit(parent); + editor->setFilter(m_filter); + + auto syncEditor = [=] { + editor->setFileUrl(property->value().toUrl()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &FileEdit::fileUrlChanged, property, &Property::setValue); + + return editor; + } + + void setFilter(const QString &filter) + { + m_filter = filter; + } + +private: + QString m_filter; +}; + class IntEditorFactory : public EditorFactory { public: @@ -581,6 +610,7 @@ ValueTypeEditorFactory::ValueTypeEditorFactory() registerEditorFactory(QMetaType::QRectF, std::make_unique()); registerEditorFactory(QMetaType::QSize, std::make_unique()); registerEditorFactory(QMetaType::QString, std::make_unique()); + registerEditorFactory(QMetaType::QUrl, std::make_unique()); } void ValueTypeEditorFactory::registerEditorFactory(int type, std::unique_ptr factory) From 2f0e76a0846afda38e506c8b6ca16d49dc4e98af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 5 Sep 2024 17:21:56 +0200 Subject: [PATCH 12/78] Added properties for map objects Also in this change: * Made SpinBox and DoubleSpinBox don't respond to changing from keyboard typing immediately. * Share a single implementation of wrapping label/widget pairs for SizeEdit, SizeFEdit, PointEdit, PointFEdit and RectFEdit. * Added widget factories for QFont, QSizeF and Qt::Alignment. * Allow setting suffix on custom FloatEditorFactory, used for adding degree symbol to rotation values. A few things remain to be done: * Custom widget for editing tile object flipping flags * Try reducing size of font editor with style toggle buttons --- src/tiled/propertieswidget.cpp | 538 +++++++++++++++++++++------- src/tiled/propertieswidget.h | 3 +- src/tiled/propertyeditorwidgets.cpp | 271 +++++++++++--- src/tiled/propertyeditorwidgets.h | 136 ++++++- src/tiled/varianteditor.cpp | 360 +++++++++++-------- src/tiled/varianteditor.h | 15 + 6 files changed, 982 insertions(+), 341 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index c7d4cc67f0..d1f741ab69 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -24,6 +24,7 @@ #include "addpropertydialog.h" #include "changeimagelayerproperty.h" #include "changelayer.h" +#include "changemapobject.h" #include "changemapproperty.h" #include "changeobjectgroupproperties.h" #include "changeproperties.h" @@ -31,6 +32,7 @@ #include "compression.h" #include "mapdocument.h" #include "objectgroup.h" +#include "objecttemplate.h" #include "preferences.h" #include "propertybrowser.h" #include "tilesetchanges.h" @@ -307,21 +309,46 @@ class TileSizeProperty : public AbstractProperty MapDocument *mMapDocument; }; -class MapProperties : public QObject +class ObjectProperties : public QObject { Q_OBJECT public: - MapProperties(MapDocument *mapDocument, - ValueTypeEditorFactory *editorFactory, - QObject *parent = nullptr) + ObjectProperties(Document *document, Object *object, QObject *parent = nullptr) : QObject(parent) - , mMapDocument(mapDocument) - , mSizeProperty(new MapSizeProperty(mapDocument, editorFactory, this)) - , mTileSizeProperty(new TileSizeProperty(mapDocument, editorFactory, this)) + , mDocument(document) + , mObject(object) + { + mClassProperty = new ClassProperty(document, object, this); + } + + virtual void populateEditor(VariantEditor *) + { + // nothing added here due to property grouping + } + +protected: + void push(QUndoCommand *command) { - mClassProperty = new ClassProperty(mMapDocument, mMapDocument->map()); + mDocument->undoStack()->push(command); + } + + Document *mDocument; + Property *mClassProperty; + Object *mObject; +}; + + +class MapProperties : public ObjectProperties +{ + Q_OBJECT +public: + MapProperties(MapDocument *document, + ValueTypeEditorFactory *editorFactory, + QObject *parent = nullptr) + : ObjectProperties(document, document->map(), parent) + { mOrientationProperty = editorFactory->createProperty( tr("Orientation"), [this]() { @@ -329,16 +356,20 @@ class MapProperties : public QObject }, [this](const QVariant &value) { auto orientation = static_cast(value.toInt()); - push(new ChangeMapProperty(mMapDocument, orientation)); + push(new ChangeMapProperty(mapDocument(), orientation)); }); + mSizeProperty = new MapSizeProperty(mapDocument(), editorFactory, this); + + mTileSizeProperty = new TileSizeProperty(mapDocument(), editorFactory, this); + mInfiniteProperty = editorFactory->createProperty( tr("Infinite"), [this]() { return map()->infinite(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, + push(new ChangeMapProperty(mapDocument(), Map::InfiniteProperty, value.toInt())); }); @@ -349,7 +380,7 @@ class MapProperties : public QObject return map()->hexSideLength(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, + push(new ChangeMapProperty(mapDocument(), Map::HexSideLengthProperty, value.toInt())); }); @@ -361,7 +392,7 @@ class MapProperties : public QObject }, [this](const QVariant &value) { auto staggerAxis = static_cast(value.toInt()); - push(new ChangeMapProperty(mMapDocument, staggerAxis)); + push(new ChangeMapProperty(mapDocument(), staggerAxis)); }); mStaggerIndexProperty = editorFactory->createProperty( @@ -371,7 +402,7 @@ class MapProperties : public QObject }, [this](const QVariant &value) { auto staggerIndex = static_cast(value.toInt()); - push(new ChangeMapProperty(mMapDocument, staggerIndex)); + push(new ChangeMapProperty(mapDocument(), staggerIndex)); }); mParallaxOriginProperty = editorFactory->createProperty( @@ -380,7 +411,7 @@ class MapProperties : public QObject return map()->parallaxOrigin(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, value.value())); + push(new ChangeMapProperty(mapDocument(), value.value())); }); mLayerDataFormatProperty = editorFactory->createProperty( @@ -390,7 +421,7 @@ class MapProperties : public QObject }, [this](const QVariant &value) { auto layerDataFormat = static_cast(value.toInt()); - push(new ChangeMapProperty(mMapDocument, layerDataFormat)); + push(new ChangeMapProperty(mapDocument(), layerDataFormat)); }); mChunkSizeProperty = editorFactory->createProperty( @@ -399,7 +430,7 @@ class MapProperties : public QObject return map()->chunkSize(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, value.toSize())); + push(new ChangeMapProperty(mapDocument(), value.toSize())); }); mRenderOrderProperty = editorFactory->createProperty( @@ -409,7 +440,7 @@ class MapProperties : public QObject }, [this](const QVariant &value) { auto renderOrder = static_cast(value.toInt()); - push(new ChangeMapProperty(mMapDocument, renderOrder)); + push(new ChangeMapProperty(mapDocument(), renderOrder)); }); mCompressionLevelProperty = editorFactory->createProperty( @@ -418,7 +449,7 @@ class MapProperties : public QObject return map()->compressionLevel(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, value.toInt())); + push(new ChangeMapProperty(mapDocument(), value.toInt())); }); mBackgroundColorProperty = editorFactory->createProperty( @@ -427,15 +458,15 @@ class MapProperties : public QObject return map()->backgroundColor(); }, [this](const QVariant &value) { - push(new ChangeMapProperty(mMapDocument, value.value())); + push(new ChangeMapProperty(mapDocument(), value.value())); }); updateEnabledState(); - connect(mMapDocument, &Document::changed, + connect(document, &Document::changed, this, &MapProperties::onChanged); } - void populateEditor(VariantEditor *editor) + void populateEditor(VariantEditor *editor) override { editor->addHeader(tr("Map")); editor->addProperty(mClassProperty); @@ -533,18 +564,16 @@ class MapProperties : public QObject } } - void push(QUndoCommand *command) + MapDocument *mapDocument() const { - mMapDocument->undoStack()->push(command); + return static_cast(mDocument); } Map *map() const { - return mMapDocument->map(); + return mapDocument()->map(); } - MapDocument *mMapDocument; - Property *mClassProperty; Property *mOrientationProperty; Property *mSizeProperty; Property *mTileSizeProperty; @@ -560,21 +589,19 @@ class MapProperties : public QObject Property *mBackgroundColorProperty; }; -class LayerProperties : public QObject +class LayerProperties : public ObjectProperties { Q_OBJECT public: - LayerProperties(MapDocument *mapDocument, Layer *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : QObject(parent) - , mMapDocument(mapDocument) - , mLayer(layer) + LayerProperties(MapDocument *document, Layer *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : ObjectProperties(document, object, parent) { // todo: would be nicer to avoid the SpinBox and use a custom widget // might also be nice to embed this in the header instead of using a property mIdProperty = editorFactory->createProperty( tr("ID"), - [this]() { return mLayer->id(); }, + [this]() { return layer()->id(); }, [](const QVariant &) {}); mIdProperty->setEnabled(false); @@ -582,63 +609,61 @@ class LayerProperties : public QObject mNameProperty = editorFactory->createProperty( tr("Name"), - [this]() { return mLayer->name(); }, + [this]() { return layer()->name(); }, [this](const QVariant &value) { - push(new SetLayerName(mMapDocument, { mLayer }, value.toString())); + push(new SetLayerName(mapDocument(), { layer() }, value.toString())); }); - mClassProperty = new ClassProperty(mMapDocument, mLayer); - mVisibleProperty = editorFactory->createProperty( tr("Visible"), - [this]() { return mLayer->isVisible(); }, + [this]() { return layer()->isVisible(); }, [this](const QVariant &value) { - push(new SetLayerVisible(mMapDocument, { mLayer }, value.toBool())); + push(new SetLayerVisible(mapDocument(), { layer() }, value.toBool())); }); mLockedProperty = editorFactory->createProperty( tr("Locked"), - [this]() { return mLayer->isLocked(); }, + [this]() { return layer()->isLocked(); }, [this](const QVariant &value) { - push(new SetLayerLocked(mMapDocument, { mLayer }, value.toBool())); + push(new SetLayerLocked(mapDocument(), { layer() }, value.toBool())); }); // todo: value should be between 0 and 1, and would be nice to use a slider (replacing the one in Layers view) // todo: singleStep should be 0.1 mOpacityProperty = editorFactory->createProperty( tr("Opacity"), - [this]() { return mLayer->opacity(); }, + [this]() { return layer()->opacity(); }, [this](const QVariant &value) { - push(new SetLayerOpacity(mMapDocument, { mLayer }, value.toReal())); + push(new SetLayerOpacity(mapDocument(), { layer() }, value.toReal())); }); mTintColorProperty = editorFactory->createProperty( tr("Tint Color"), - [this]() { return mLayer->tintColor(); }, + [this]() { return layer()->tintColor(); }, [this](const QVariant &value) { - push(new SetLayerTintColor(mMapDocument, { mLayer }, value.value())); + push(new SetLayerTintColor(mapDocument(), { layer() }, value.value())); }); mOffsetProperty = editorFactory->createProperty( tr("Offset"), - [this]() { return mLayer->offset(); }, + [this]() { return layer()->offset(); }, [this](const QVariant &value) { - push(new SetLayerOffset(mMapDocument, { mLayer }, value.value())); + push(new SetLayerOffset(mapDocument(), { layer() }, value.value())); }); // todo: singleStep should be 0.1 mParallaxFactorProperty = editorFactory->createProperty( tr("Parallax Factor"), - [this]() { return mLayer->parallaxFactor(); }, + [this]() { return layer()->parallaxFactor(); }, [this](const QVariant &value) { - push(new SetLayerParallaxFactor(mMapDocument, { mLayer }, value.toPointF())); + push(new SetLayerParallaxFactor(mapDocument(), { layer() }, value.toPointF())); }); - connect(mMapDocument, &Document::changed, + connect(document, &Document::changed, this, &LayerProperties::onChanged); } - virtual void populateEditor(VariantEditor *editor) + void populateEditor(VariantEditor *editor) override { editor->addHeader(tr("Layer")); editor->addProperty(mIdProperty); @@ -660,7 +685,7 @@ class LayerProperties : public QObject return; const auto &layerChange = static_cast(event); - if (layerChange.layer != mLayer) + if (layerChange.layer != layer()) return; if (layerChange.properties & LayerChangeEvent::VisibleProperty) @@ -677,17 +702,18 @@ class LayerProperties : public QObject emit mParallaxFactorProperty->valueChanged(); } - void push(QUndoCommand *command) + MapDocument *mapDocument() const { - mMapDocument->undoStack()->push(command); + return static_cast(mDocument); } - MapDocument *mMapDocument; - Layer *mLayer; + Layer *layer() const + { + return static_cast(mObject); + } Property *mIdProperty; Property *mNameProperty; - Property *mClassProperty; Property *mVisibleProperty; Property *mLockedProperty; Property *mOpacityProperty; @@ -701,22 +727,22 @@ class ImageLayerProperties : public LayerProperties Q_OBJECT public: - ImageLayerProperties(MapDocument *mapDocument, ImageLayer *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : LayerProperties(mapDocument, layer, editorFactory, parent) + ImageLayerProperties(MapDocument *document, ImageLayer *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : LayerProperties(document, object, editorFactory, parent) { // todo: set a file filter for selecting images (or map files?) mImageProperty = editorFactory->createProperty( tr("Image Source"), [this]() { return imageLayer()->imageSource(); }, [this](const QVariant &value) { - push(new ChangeImageLayerImageSource(mMapDocument, { imageLayer() }, value.toUrl())); + push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value.toUrl())); }); mTransparentColorProperty = editorFactory->createProperty( tr("Transparent Color"), [this]() { return imageLayer()->transparentColor(); }, [this](const QVariant &value) { - push(new ChangeImageLayerTransparentColor(mMapDocument, { imageLayer() }, value.value())); + push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value.value())); }); // todo: consider merging Repeat X and Y into a single property @@ -724,14 +750,14 @@ class ImageLayerProperties : public LayerProperties tr("Repeat X"), [this]() { return imageLayer()->repeatX(); }, [this](const QVariant &value) { - push(new ChangeImageLayerRepeatX(mMapDocument, { imageLayer() }, value.toBool())); + push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, value.toBool())); }); mRepeatYProperty = editorFactory->createProperty( tr("Repeat Y"), [this]() { return imageLayer()->repeatY(); }, [this](const QVariant &value) { - push(new ChangeImageLayerRepeatY(mMapDocument, { imageLayer() }, value.toBool())); + push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, value.toBool())); }); } @@ -755,7 +781,7 @@ class ImageLayerProperties : public LayerProperties return; const auto &layerChange = static_cast(event); - if (layerChange.layer != mLayer) + if (layerChange.layer != layer()) return; if (layerChange.properties & ImageLayerChangeEvent::ImageSourceProperty) @@ -770,7 +796,7 @@ class ImageLayerProperties : public LayerProperties ImageLayer *imageLayer() const { - return static_cast(mLayer); + return static_cast(mObject); } Property *mImageProperty; @@ -784,14 +810,14 @@ class ObjectGroupProperties : public LayerProperties Q_OBJECT public: - ObjectGroupProperties(MapDocument *mapDocument, ObjectGroup *layer, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : LayerProperties(mapDocument, layer, editorFactory, parent) + ObjectGroupProperties(MapDocument *document, ObjectGroup *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : LayerProperties(document, object, editorFactory, parent) { mColorProperty = editorFactory->createProperty( tr("Color"), [this]() { return objectGroup()->color(); }, [this](const QVariant &value) { - push(new ChangeObjectGroupColor(mMapDocument, { objectGroup() }, value.value())); + push(new ChangeObjectGroupColor(mapDocument(), { objectGroup() }, value.value())); }); mDrawOrderProperty = editorFactory->createProperty( @@ -799,7 +825,7 @@ class ObjectGroupProperties : public LayerProperties [this]() { return QVariant::fromValue(objectGroup()->drawOrder()); }, [this](const QVariant &value) { ObjectGroup::DrawOrder drawOrder = static_cast(value.toInt()); - push(new ChangeObjectGroupDrawOrder(mMapDocument, { objectGroup() }, drawOrder)); + push(new ChangeObjectGroupDrawOrder(mapDocument(), { objectGroup() }, drawOrder)); }); } @@ -831,35 +857,32 @@ class ObjectGroupProperties : public LayerProperties ObjectGroup *objectGroup() const { - return static_cast(mLayer); + return static_cast(mObject); } Property *mColorProperty; Property *mDrawOrderProperty; }; -class TilesetProperties : public QObject +class TilesetProperties : public ObjectProperties { Q_OBJECT public: - TilesetProperties(TilesetDocument *tilesetDocument, + TilesetProperties(TilesetDocument *document, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : QObject(parent) - , mTilesetDocument(tilesetDocument) + : ObjectProperties(document, document->tileset().data(), parent) { mNameProperty = editorFactory->createProperty( tr("Name"), [this]() { - return mTilesetDocument->tileset()->name(); + return tilesetDocument()->tileset()->name(); }, [this](const QVariant &value) { - push(new RenameTileset(mTilesetDocument, value.toString())); + push(new RenameTileset(tilesetDocument(), value.toString())); }); - mClassProperty = new ClassProperty(tilesetDocument, tilesetDocument->tileset().data()); - mObjectAlignmentProperty = editorFactory->createProperty( tr("Object Alignment"), [this]() { @@ -867,7 +890,7 @@ class TilesetProperties : public QObject }, [this](const QVariant &value) { const auto objectAlignment = static_cast(value.toInt()); - push(new ChangeTilesetObjectAlignment(mTilesetDocument, objectAlignment)); + push(new ChangeTilesetObjectAlignment(tilesetDocument(), objectAlignment)); }); mTileOffsetProperty = editorFactory->createProperty( @@ -876,7 +899,7 @@ class TilesetProperties : public QObject return tileset()->tileOffset(); }, [this](const QVariant &value) { - push(new ChangeTilesetTileOffset(mTilesetDocument, value.value())); + push(new ChangeTilesetTileOffset(tilesetDocument(), value.value())); }); mTileRenderSizeProperty = editorFactory->createProperty( @@ -886,7 +909,7 @@ class TilesetProperties : public QObject }, [this](const QVariant &value) { const auto tileRenderSize = static_cast(value.toInt()); - push(new ChangeTilesetTileRenderSize(mTilesetDocument, tileRenderSize)); + push(new ChangeTilesetTileRenderSize(tilesetDocument(), tileRenderSize)); }); mFillModeProperty = editorFactory->createProperty( @@ -896,7 +919,7 @@ class TilesetProperties : public QObject }, [this](const QVariant &value) { const auto fillMode = static_cast(value.toInt()); - push(new ChangeTilesetFillMode(mTilesetDocument, fillMode)); + push(new ChangeTilesetFillMode(tilesetDocument(), fillMode)); }); mBackgroundColorProperty = editorFactory->createProperty( @@ -905,7 +928,7 @@ class TilesetProperties : public QObject return tileset()->backgroundColor(); }, [this](const QVariant &value) { - push(new ChangeTilesetBackgroundColor(mTilesetDocument, value.value())); + push(new ChangeTilesetBackgroundColor(tilesetDocument(), value.value())); }); mOrientationProperty = editorFactory->createProperty( @@ -915,7 +938,7 @@ class TilesetProperties : public QObject }, [this](const QVariant &value) { const auto orientation = static_cast(value.toInt()); - push(new ChangeTilesetOrientation(mTilesetDocument, orientation)); + push(new ChangeTilesetOrientation(tilesetDocument(), orientation)); }); mGridSizeProperty = editorFactory->createProperty( @@ -924,7 +947,7 @@ class TilesetProperties : public QObject return tileset()->gridSize(); }, [this](const QVariant &value) { - push(new ChangeTilesetGridSize(mTilesetDocument, value.toSize())); + push(new ChangeTilesetGridSize(tilesetDocument(), value.toSize())); }); // todo: needs 1 as minimum value @@ -934,7 +957,7 @@ class TilesetProperties : public QObject return tileset()->columnCount(); }, [this](const QVariant &value) { - push(new ChangeTilesetColumnCount(mTilesetDocument, value.toInt())); + push(new ChangeTilesetColumnCount(tilesetDocument(), value.toInt())); }); // todo: this needs a custom widget @@ -945,7 +968,7 @@ class TilesetProperties : public QObject }, [this](const QVariant &value) { const auto flags = static_cast(value.toInt()); - push(new ChangeTilesetTransformationFlags(mTilesetDocument, flags)); + push(new ChangeTilesetTransformationFlags(tilesetDocument(), flags)); }); // todo: this needs a custom widget @@ -955,20 +978,20 @@ class TilesetProperties : public QObject return tileset()->imageSource().toString(); }, [](const QVariant &) { - // push(new ChangeTilesetImage(mTilesetDocument, value.toString())); + // push(new ChangeTilesetImage(tilesetDocument(), value.toString())); }); updateEnabledState(); - connect(mTilesetDocument, &Document::changed, + connect(tilesetDocument(), &Document::changed, this, &TilesetProperties::onChanged); - connect(mTilesetDocument, &TilesetDocument::tilesetNameChanged, + connect(tilesetDocument(), &TilesetDocument::tilesetNameChanged, mNameProperty, &Property::valueChanged); - connect(mTilesetDocument, &TilesetDocument::tilesetTileOffsetChanged, + connect(tilesetDocument(), &TilesetDocument::tilesetTileOffsetChanged, mTileOffsetProperty, &Property::valueChanged); - connect(mTilesetDocument, &TilesetDocument::tilesetObjectAlignmentChanged, + connect(tilesetDocument(), &TilesetDocument::tilesetObjectAlignmentChanged, mObjectAlignmentProperty, &Property::valueChanged); - connect(mTilesetDocument, &TilesetDocument::tilesetChanged, + connect(tilesetDocument(), &TilesetDocument::tilesetChanged, this, &TilesetProperties::onTilesetChanged); } @@ -1025,19 +1048,17 @@ class TilesetProperties : public QObject mColumnCountProperty->setEnabled(collection); } - void push(QUndoCommand *command) + TilesetDocument *tilesetDocument() const { - mTilesetDocument->undoStack()->push(command); + return static_cast(mDocument); } Tileset *tileset() const { - return mTilesetDocument->tileset().data(); + return tilesetDocument()->tileset().data(); } - TilesetDocument *mTilesetDocument; Property *mNameProperty; - Property *mClassProperty; Property *mObjectAlignmentProperty; Property *mTileOffsetProperty; Property *mTileRenderSizeProperty; @@ -1050,10 +1071,279 @@ class TilesetProperties : public QObject Property *mImageProperty; }; +class MapObjectProperties : public ObjectProperties +{ + Q_OBJECT + +public: + MapObjectProperties(MapDocument *document, MapObject *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : ObjectProperties(document, object, parent) + , mDegreesEditorFactory(std::make_unique()) + { + mDegreesEditorFactory->setSuffix(QStringLiteral("°")); + + mIdProperty = editorFactory->createProperty( + tr("ID"), + [this]() { return mapObject()->id(); }, + [](const QVariant &) {}); + mIdProperty->setEnabled(false); + + mTemplateProperty = editorFactory->createProperty( + tr("Template"), + [this]() { + if (auto objectTemplate = mapObject()->objectTemplate()) + return QUrl::fromLocalFile(objectTemplate->fileName()); + return QUrl(); + }, + [](const QVariant &) {}); + mTemplateProperty->setEnabled(false); + + mNameProperty = editorFactory->createProperty( + tr("Name"), + [this]() { + return mapObject()->name(); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::NameProperty, value); + }); + + mVisibleProperty = editorFactory->createProperty( + tr("Visible"), + [this]() { + return mapObject()->isVisible(); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::VisibleProperty, value); + }); + + mPositionProperty = editorFactory->createProperty( + tr("Position"), + [this]() { + return mapObject()->position(); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::PositionProperty, value); + }); + + mSizeProperty = editorFactory->createProperty( + tr("Size"), + [this]() { + return mapObject()->size(); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::SizeProperty, value); + }); + + mRotationProperty = new GetSetProperty( + tr("Rotation"), + [this]() { + return mapObject()->rotation(); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::RotationProperty, value); + }, + mDegreesEditorFactory.get(), this); + + // todo: make this a custom widget with "Horizontal" and "Vertical" checkboxes + mFlippingProperty = editorFactory->createProperty( + tr("Flipping"), + [this]() { + return mapObject()->cell().flags(); + }, + [this](const QVariant &value) { + const int flippingFlags = value.toInt(); + + MapObjectCell mapObjectCell; + mapObjectCell.object = mapObject(); + mapObjectCell.cell = mapObject()->cell(); + mapObjectCell.cell.setFlippedHorizontally(flippingFlags & 1); + mapObjectCell.cell.setFlippedVertically(flippingFlags & 2); + + auto command = new ChangeMapObjectCells(mDocument, { mapObjectCell }); + + command->setText(QCoreApplication::translate("Undo Commands", + "Flip %n Object(s)", + nullptr, + mapDocument()->selectedObjects().size())); + push(command); + }); + + mTextProperty = editorFactory->createProperty( + tr("Text"), + [this]() { + return mapObject()->textData().text; + }, + [this](const QVariant &value) { + changeMapObject(MapObject::TextProperty, value); + }); + + mTextAlignmentProperty = editorFactory->createProperty( + tr("Alignment"), + [this]() { + return QVariant::fromValue(mapObject()->textData().alignment); + }, + [this](const QVariant &value) { + changeMapObject(MapObject::TextAlignmentProperty, value); + }); + + mTextFontProperty = editorFactory->createProperty( + tr("Font"), + [this]() { + return mapObject()->textData().font; + }, + [this](const QVariant &value) { + changeMapObject(MapObject::TextFontProperty, value); + }); + + mTextWordWrapProperty = editorFactory->createProperty( + tr("Word Wrap"), + [this]() { + return mapObject()->textData().wordWrap; + }, + [this](const QVariant &value) { + changeMapObject(MapObject::TextWordWrapProperty, value); + }); + + mTextColorProperty = editorFactory->createProperty( + tr("Text Color"), + [this]() { + return mapObject()->textData().color; + }, + [this](const QVariant &value) { + changeMapObject(MapObject::TextColorProperty, value); + }); + + connect(document, &Document::changed, + this, &MapObjectProperties::onChanged); + + updateEnabledState(); + } + + void populateEditor(VariantEditor *editor) override + { + editor->addHeader(tr("Object")); + editor->addProperty(mIdProperty); + editor->addProperty(mTemplateProperty); + editor->addProperty(mNameProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + + if (mapDocument()->allowHidingObjects()) + editor->addProperty(mVisibleProperty); + + editor->addProperty(mPositionProperty); + + if (mapObject()->hasDimensions()) + editor->addProperty(mSizeProperty); + + if (mapObject()->canRotate()) + editor->addProperty(mRotationProperty); + + if (mapObject()->isTileObject()) { + editor->addSeparator(); + editor->addProperty(mFlippingProperty); + } + + if (mapObject()->shape() == MapObject::Text) { + editor->addSeparator(); + editor->addProperty(mTextProperty); + editor->addProperty(mTextAlignmentProperty); + editor->addProperty(mTextFontProperty); + editor->addProperty(mTextWordWrapProperty); + editor->addProperty(mTextColorProperty); + } + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::MapObjectsChanged) + return; + + const auto &change = static_cast(event); + if (!change.mapObjects.contains(mapObject())) + return; + + if (change.properties & MapObject::NameProperty) + emit mNameProperty->valueChanged(); + if (change.properties & MapObject::VisibleProperty) + emit mVisibleProperty->valueChanged(); + if (change.properties & MapObject::PositionProperty) + emit mPositionProperty->valueChanged(); + if (change.properties & MapObject::SizeProperty) + emit mSizeProperty->valueChanged(); + if (change.properties & MapObject::RotationProperty) + emit mRotationProperty->valueChanged(); + if (change.properties & MapObject::CellProperty) + emit mFlippingProperty->valueChanged(); + if (change.properties & MapObject::TextProperty) + emit mTextProperty->valueChanged(); + if (change.properties & MapObject::TextFontProperty) + emit mTextFontProperty->valueChanged(); + if (change.properties & MapObject::TextAlignmentProperty) + emit mTextAlignmentProperty->valueChanged(); + if (change.properties & MapObject::TextWordWrapProperty) + emit mTextWordWrapProperty->valueChanged(); + if (change.properties & MapObject::TextColorProperty) + emit mTextColorProperty->valueChanged(); + } + + void updateEnabledState() + { + mVisibleProperty->setEnabled(mapDocument()->allowHidingObjects()); + mSizeProperty->setEnabled(mapObject()->hasDimensions()); + mRotationProperty->setEnabled(mapObject()->canRotate()); + mFlippingProperty->setEnabled(mapObject()->isTileObject()); + + const bool isText = mapObject()->shape() == MapObject::Text; + mTextProperty->setEnabled(isText); + mTextAlignmentProperty->setEnabled(isText); + mTextFontProperty->setEnabled(isText); + mTextWordWrapProperty->setEnabled(isText); + mTextColorProperty->setEnabled(isText); + } + + MapDocument *mapDocument() const + { + return static_cast(mDocument); + } + + MapObject *mapObject() const + { + return static_cast(mObject); + } + + void changeMapObject(MapObject::Property property, const QVariant &value) + { + push(new ChangeMapObject(mapDocument(), mapObject(), property, value)); + } + + Property *mIdProperty; + Property *mTemplateProperty; + Property *mNameProperty; + Property *mVisibleProperty; + Property *mPositionProperty; + Property *mSizeProperty; + Property *mRotationProperty; + + // for tile objects + Property *mFlippingProperty; + + // for text objects + Property *mTextProperty; + Property *mTextAlignmentProperty; + Property *mTextFontProperty; + Property *mTextWordWrapProperty; + Property *mTextColorProperty; + + std::unique_ptr mDegreesEditorFactory; +}; + void PropertiesWidget::currentObjectChanged(Object *object) { mPropertyBrowser->clear(); + delete mPropertiesObject; mPropertiesObject = nullptr; @@ -1061,56 +1351,54 @@ void PropertiesWidget::currentObjectChanged(Object *object) switch (object->typeId()) { case Object::LayerType: { auto mapDocument = static_cast(mDocument); - auto layer = static_cast(object); - LayerProperties *layerProperties = nullptr; - switch (layer->layerType()) { + switch (static_cast(object)->layerType()) { case Layer::ImageLayerType: - layerProperties = new ImageLayerProperties(mapDocument, - static_cast(layer), - mDefaultEditorFactory.get(), this); + mPropertiesObject = new ImageLayerProperties(mapDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); break; case Layer::ObjectGroupType: - layerProperties = new ObjectGroupProperties(mapDocument, - static_cast(layer), - mDefaultEditorFactory.get(), this); + mPropertiesObject = new ObjectGroupProperties(mapDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); break; case Layer::TileLayerType: case Layer::GroupLayerType: - layerProperties = new LayerProperties(mapDocument, - layer, - mDefaultEditorFactory.get(), this); + mPropertiesObject = new LayerProperties(mapDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); break; } - - layerProperties->populateEditor(mPropertyBrowser); - mPropertiesObject = layerProperties; + break; } case Object::MapObjectType: + mPropertiesObject = new MapObjectProperties(static_cast(mDocument), + static_cast(object), mDefaultEditorFactory.get(), this); break; - case Object::MapType: { - auto mapDocument = static_cast(mDocument); - auto properties = new MapProperties(mapDocument, mDefaultEditorFactory.get(), this); - properties->populateEditor(mPropertyBrowser); - mPropertiesObject = properties; + case Object::MapType: + mPropertiesObject = new MapProperties(static_cast(mDocument), + mDefaultEditorFactory.get(), this); + break; + case Object::TilesetType: + mPropertiesObject = new TilesetProperties(static_cast(mDocument), + mDefaultEditorFactory.get(), this); break; - } - case Object::TilesetType: { - auto tilesetDocument = static_cast(mDocument); - auto properties = new TilesetProperties(tilesetDocument, - mDefaultEditorFactory.get(), this); - properties->populateEditor(mPropertyBrowser); - mPropertiesObject = properties; - } case Object::TileType: + // todo case Object::WangSetType: + // todo case Object::WangColorType: + // todo case Object::ProjectType: case Object::WorldType: break; } } + if (mPropertiesObject) + mPropertiesObject->populateEditor(mPropertyBrowser); + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; bool isTileset = object && object->isPartOfTileset(); bool enabled = object && (!isTileset || editingTileset); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 37db77ee6d..d6ad390af1 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -30,6 +30,7 @@ class Object; class Document; class EditorFactory; +class ObjectProperties; class ValueTypeEditorFactory; class VariantEditor; @@ -81,7 +82,7 @@ public slots: void retranslateUi(); Document *mDocument = nullptr; - QObject *mPropertiesObject = nullptr; + ObjectProperties *mPropertiesObject = nullptr; VariantEditor *mPropertyBrowser; std::unique_ptr mDefaultEditorFactory; QAction *mActionAddProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 36e899a3b0..3bb86cb855 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -41,6 +41,9 @@ SpinBox::SpinBox(QWidget *parent) setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + // Don't respond to keyboard input immediately. + setKeyboardTracking(false); + // Allow the widget to shrink horizontally. setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } @@ -64,6 +67,9 @@ DoubleSpinBox::DoubleSpinBox(QWidget *parent) // Increase possible precision. setDecimals(9); + // Don't respond to keyboard input immediately. + setKeyboardTracking(false); + // Allow the widget to shrink horizontally. setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } @@ -88,36 +94,114 @@ QString DoubleSpinBox::textFromValue(double val) const } -SizeEdit::SizeEdit(QWidget *parent) +ResponsivePairswiseWidget::ResponsivePairswiseWidget(QWidget *parent) : QWidget(parent) - , m_widthLabel(new QLabel(QStringLiteral("W"), this)) - , m_heightLabel(new QLabel(QStringLiteral("H"), this)) - , m_widthSpinBox(new SpinBox(this)) - , m_heightSpinBox(new SpinBox(this)) { - m_widthLabel->setAlignment(Qt::AlignCenter); - m_heightLabel->setAlignment(Qt::AlignCenter); - auto layout = new QGridLayout(this); layout->setContentsMargins(QMargins()); layout->setColumnStretch(1, 1); - layout->setColumnStretch(3, 1); layout->setSpacing(Utils::dpiScaled(3)); +} +void ResponsivePairswiseWidget::setWidgetPairs(const QVector &widgetPairs) +{ const int horizontalMargin = Utils::dpiScaled(3); - m_widthLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - m_heightLabel->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); + for (auto &pair : widgetPairs) { + pair.label->setAlignment(Qt::AlignCenter); + pair.label->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + } + + m_widgetPairs = widgetPairs; + + addWidgetsToLayout(); +} + +void ResponsivePairswiseWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + + const auto orientation = event->size().width() < minimumHorizontalWidth() + ? Qt::Vertical : Qt::Horizontal; + + if (m_orientation != orientation) { + m_orientation = orientation; + + auto layout = this->layout(); + + // Remove all widgets from layout, without deleting them + for (auto &pair : m_widgetPairs) { + layout->removeWidget(pair.label); + layout->removeWidget(pair.widget); + } + + addWidgetsToLayout(); + + // This avoids flickering when the layout changes + layout->activate(); + } +} + +void ResponsivePairswiseWidget::addWidgetsToLayout() +{ + auto layout = qobject_cast(this->layout()); + + const int maxColumns = m_orientation == Qt::Horizontal ? 4 : 2; + int row = 0; + int column = 0; + + for (auto &pair : m_widgetPairs) { + layout->addWidget(pair.label, row, column); + layout->addWidget(pair.widget, row, column + 1); + column += 2; + + if (column == maxColumns) { + column = 0; + ++row; + } + } + + layout->setColumnStretch(3, m_orientation == Qt::Horizontal ? 1 : 0); +} + +int ResponsivePairswiseWidget::minimumHorizontalWidth() const +{ + const int spacing = layout()->spacing(); + int sum = 0; + int minimum = 0; + int index = 0; + + for (auto &pair : m_widgetPairs) { + sum += (pair.label->minimumSizeHint().width() + + pair.widget->minimumSizeHint().width() + + spacing * 2); + + if (++index % 2 == 0) { + minimum = std::max(sum - spacing, minimum); + sum = 0; + } + } + + return minimum; +} + + +SizeEdit::SizeEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_widthSpinBox(new SpinBox(this)) + , m_heightSpinBox(new SpinBox(this)) +{ + setWidgetPairs({ + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }); connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); } - void SizeEdit::setValue(const QSize &size) { m_widthSpinBox->setValue(size.width()); @@ -126,53 +210,136 @@ void SizeEdit::setValue(const QSize &size) QSize SizeEdit::value() const { - return QSize(m_widthSpinBox->value(), m_heightSpinBox->value()); + return QSize(m_widthSpinBox->value(), + m_heightSpinBox->value()); } -void SizeEdit::resizeEvent(QResizeEvent *event) + +SizeFEdit::SizeFEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_widthSpinBox(new DoubleSpinBox(this)) + , m_heightSpinBox(new DoubleSpinBox(this)) { - QWidget::resizeEvent(event); + setWidgetPairs({ + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }); - const auto orientation = event->size().width() < minimumHorizontalWidth() - ? Qt::Vertical : Qt::Horizontal; + connect(m_widthSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &SizeFEdit::valueChanged); + connect(m_heightSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &SizeFEdit::valueChanged); +} - if (m_orientation != orientation) { - m_orientation = orientation; +void SizeFEdit::setValue(const QSizeF &size) +{ + m_widthSpinBox->setValue(size.width()); + m_heightSpinBox->setValue(size.height()); +} - auto layout = qobject_cast(this->layout()); +QSizeF SizeFEdit::value() const +{ + return QSizeF(m_widthSpinBox->value(), + m_heightSpinBox->value()); +} - // Remove all widgets from layout, without deleting them - layout->removeWidget(m_widthLabel); - layout->removeWidget(m_widthSpinBox); - layout->removeWidget(m_heightLabel); - layout->removeWidget(m_heightSpinBox); - - if (orientation == Qt::Horizontal) { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 0, 2); - layout->addWidget(m_heightSpinBox, 0, 3); - layout->setColumnStretch(3, 1); - } else { - layout->addWidget(m_widthLabel, 0, 0); - layout->addWidget(m_widthSpinBox, 0, 1); - layout->addWidget(m_heightLabel, 1, 0); - layout->addWidget(m_heightSpinBox, 1, 1); - layout->setColumnStretch(3, 0); - } - // this avoids flickering when the layout changes - layout->activate(); - } +PointEdit::PointEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_xLabel(new QLabel(QStringLiteral("X"), this)) + , m_yLabel(new QLabel(QStringLiteral("Y"), this)) + , m_xSpinBox(new SpinBox(this)) + , m_ySpinBox(new SpinBox(this)) +{ + setWidgetPairs({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + }); + + connect(m_xSpinBox, qOverload(&QSpinBox::valueChanged), this, &PointEdit::valueChanged); + connect(m_ySpinBox, qOverload(&QSpinBox::valueChanged), this, &PointEdit::valueChanged); +} + +void PointEdit::setValue(const QPoint &point) +{ + m_xSpinBox->setValue(point.x()); + m_ySpinBox->setValue(point.y()); +} + +QPoint PointEdit::value() const +{ + return QPoint(m_xSpinBox->value(), + m_ySpinBox->value()); +} + + +PointFEdit::PointFEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_xLabel(new QLabel(QStringLiteral("X"), this)) + , m_yLabel(new QLabel(QStringLiteral("Y"), this)) + , m_xSpinBox(new DoubleSpinBox(this)) + , m_ySpinBox(new DoubleSpinBox(this)) +{ + setWidgetPairs({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + }); + + connect(m_xSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &PointFEdit::valueChanged); + connect(m_ySpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &PointFEdit::valueChanged); +} + +void PointFEdit::setValue(const QPointF &point) +{ + m_xSpinBox->setValue(point.x()); + m_ySpinBox->setValue(point.y()); +} + +QPointF PointFEdit::value() const +{ + return QPointF(m_xSpinBox->value(), + m_ySpinBox->value()); +} + + +RectFEdit::RectFEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_xLabel(new QLabel(QStringLiteral("X"), this)) + , m_yLabel(new QLabel(QStringLiteral("Y"), this)) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_xSpinBox(new DoubleSpinBox(this)) + , m_ySpinBox(new DoubleSpinBox(this)) + , m_widthSpinBox(new DoubleSpinBox(this)) + , m_heightSpinBox(new DoubleSpinBox(this)) +{ + setWidgetPairs({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }); + + connect(m_xSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); + connect(m_ySpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); + connect(m_widthSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); + connect(m_heightSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); +} + +void RectFEdit::setValue(const QRectF &rect) +{ + m_xSpinBox->setValue(rect.x()); + m_ySpinBox->setValue(rect.y()); + m_widthSpinBox->setValue(rect.width()); + m_heightSpinBox->setValue(rect.height()); } -int SizeEdit::minimumHorizontalWidth() const +QRectF RectFEdit::value() const { - return m_widthLabel->minimumSizeHint().width() + - m_widthSpinBox->minimumSizeHint().width() + - m_heightLabel->minimumSizeHint().width() + - m_heightSpinBox->minimumSizeHint().width() + - layout()->spacing() * 3; + return QRectF(m_xSpinBox->value(), + m_ySpinBox->value(), + m_widthSpinBox->value(), + m_heightSpinBox->value()); } diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index f33eaf7fbd..342b0b505a 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -41,10 +41,39 @@ class DoubleSpinBox : public QDoubleSpinBox QString textFromValue(double val) const override; }; +/** + * A widget that shows label/widget pairs, wrapping them either two per row + * or each on their own row, depending on the available space. + */ +class ResponsivePairswiseWidget : public QWidget +{ + Q_OBJECT + +public: + struct WidgetPair { + QLabel *label; + QWidget *widget; + }; + + ResponsivePairswiseWidget(QWidget *parent = nullptr); + + void setWidgetPairs(const QVector &widgetPairs); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + void addWidgetsToLayout(); + int minimumHorizontalWidth() const; + + Qt::Orientation m_orientation = Qt::Horizontal; + QVector m_widgetPairs; +}; + /** * A widget for editing a QSize value. */ -class SizeEdit : public QWidget +class SizeEdit : public ResponsivePairswiseWidget { Q_OBJECT Q_PROPERTY(QSize value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -59,17 +88,112 @@ class SizeEdit : public QWidget void valueChanged(); private: - void resizeEvent(QResizeEvent *event) override; - - int minimumHorizontalWidth() const; - - Qt::Orientation m_orientation = Qt::Horizontal; QLabel *m_widthLabel; QLabel *m_heightLabel; SpinBox *m_widthSpinBox; SpinBox *m_heightSpinBox; }; +/** + * A widget for editing a QSizeF value. + */ +class SizeFEdit : public ResponsivePairswiseWidget +{ + Q_OBJECT + Q_PROPERTY(QSizeF value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + SizeFEdit(QWidget *parent = nullptr); + + void setValue(const QSizeF &size); + QSizeF value() const; + +signals: + void valueChanged(); + +private: + QLabel *m_widthLabel; + QLabel *m_heightLabel; + DoubleSpinBox *m_widthSpinBox; + DoubleSpinBox *m_heightSpinBox; +}; + +/** + * A widget for editing a QPoint value. + */ +class PointEdit : public ResponsivePairswiseWidget +{ + Q_OBJECT + Q_PROPERTY(QPoint value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + PointEdit(QWidget *parent = nullptr); + + void setValue(const QPoint &size); + QPoint value() const; + +signals: + void valueChanged(); + +private: + QLabel *m_xLabel; + QLabel *m_yLabel; + SpinBox *m_xSpinBox; + SpinBox *m_ySpinBox; +}; + +/** + * A widget for editing a QPointF value. + */ +class PointFEdit : public ResponsivePairswiseWidget +{ + Q_OBJECT + Q_PROPERTY(QPointF value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + PointFEdit(QWidget *parent = nullptr); + + void setValue(const QPointF &size); + QPointF value() const; + +signals: + void valueChanged(); + +private: + QLabel *m_xLabel; + QLabel *m_yLabel; + DoubleSpinBox *m_xSpinBox; + DoubleSpinBox *m_ySpinBox; +}; + +/** + * A widget for editing a QRectF value. + */ +class RectFEdit : public ResponsivePairswiseWidget +{ + Q_OBJECT + Q_PROPERTY(QRectF value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + RectFEdit(QWidget *parent = nullptr); + + void setValue(const QRectF &size); + QRectF value() const; + +signals: + void valueChanged(); + +private: + QLabel *m_xLabel; + QLabel *m_yLabel; + QLabel *m_widthLabel; + QLabel *m_heightLabel; + DoubleSpinBox *m_xSpinBox; + DoubleSpinBox *m_ySpinBox; + DoubleSpinBox *m_widthSpinBox; + DoubleSpinBox *m_heightSpinBox; +}; + /** * A label that elides its text if there is not enough space. * diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 854b52d5c7..66ef71b144 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -111,45 +112,41 @@ class UrlEditorFactory : public EditorFactory QString m_filter; }; -class IntEditorFactory : public EditorFactory +QWidget *IntEditorFactory::createEditor(Property *property, QWidget *parent) { -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new SpinBox(parent); - auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toInt()); - }; - syncEditor(); + auto editor = new SpinBox(parent); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toInt()); + }; + syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, qOverload(&SpinBox::valueChanged), - property, &Property::setValue); + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&SpinBox::valueChanged), + property, &Property::setValue); + + return editor; +} - return editor; - } -}; -class FloatEditorFactory : public EditorFactory +QWidget *FloatEditorFactory::createEditor(Property *property, QWidget *parent) { -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new DoubleSpinBox(parent); - auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toDouble()); - }; - syncEditor(); + auto editor = new DoubleSpinBox(parent); + editor->setSuffix(m_suffix); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), - property, &Property::setValue); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toDouble()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), + property, &Property::setValue); + + return editor; +} - return editor; - } -}; class BoolEditorFactory : public EditorFactory { @@ -175,99 +172,50 @@ class BoolEditorFactory : public EditorFactory } }; -// todo: implement responsive layout (see SizeEdit) class PointEditorFactory : public EditorFactory { public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto editor = new QWidget(parent); - auto horizontalLayout = new QHBoxLayout(editor); - horizontalLayout->setContentsMargins(QMargins()); - - auto xLabel = new QLabel(QStringLiteral("X"), editor); - horizontalLayout->addWidget(xLabel, 0, Qt::AlignRight); - - auto xSpinBox = new SpinBox(editor); - xLabel->setBuddy(xSpinBox); - horizontalLayout->addWidget(xSpinBox, 1); - - auto yLabel = new QLabel(QStringLiteral("Y"), editor); - horizontalLayout->addWidget(yLabel, 0, Qt::AlignRight); - - auto ySpinBox = new SpinBox(editor); - yLabel->setBuddy(ySpinBox); - horizontalLayout->addWidget(ySpinBox, 1); - - auto syncEditor = [=] { - const QSignalBlocker xBlocker(xSpinBox); - const QSignalBlocker yBlocker(ySpinBox); - const auto point = property->value().toPoint(); - xSpinBox->setValue(point.x()); - ySpinBox->setValue(point.y()); - }; - auto syncProperty = [=] { - property->setValue(QPoint(xSpinBox->value(), ySpinBox->value())); + auto editor = new PointEdit(parent); + auto syncEditor = [property, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toPoint()); }; syncEditor(); QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(xSpinBox, qOverload(&SpinBox::valueChanged), - property, syncProperty); - QObject::connect(ySpinBox, qOverload(&SpinBox::valueChanged), - property, syncProperty); + QObject::connect(editor, &PointEdit::valueChanged, property, + [property, editor] { + property->setValue(editor->value()); + }); return editor; } }; -// todo: implement responsive layout (see SizeEdit) class PointFEditorFactory : public EditorFactory { public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto editor = new QWidget(parent); - auto horizontalLayout = new QHBoxLayout(editor); - horizontalLayout->setContentsMargins(QMargins()); - - auto xLabel = new QLabel(QStringLiteral("X"), editor); - horizontalLayout->addWidget(xLabel, 0, Qt::AlignRight); - - auto xSpinBox = new DoubleSpinBox(editor); - xLabel->setBuddy(xSpinBox); - horizontalLayout->addWidget(xSpinBox, 1); - - auto yLabel = new QLabel(QStringLiteral("Y"), editor); - horizontalLayout->addWidget(yLabel, 0, Qt::AlignRight); - - auto ySpinBox = new DoubleSpinBox(editor); - yLabel->setBuddy(ySpinBox); - horizontalLayout->addWidget(ySpinBox, 1); - - auto syncEditor = [=] { - const QSignalBlocker xBlocker(xSpinBox); - const QSignalBlocker yBlocker(ySpinBox); - const auto point = property->value().toPointF(); - xSpinBox->setValue(point.x()); - ySpinBox->setValue(point.y()); - }; - auto syncProperty = [=] { - property->setValue(QPointF(xSpinBox->value(), ySpinBox->value())); + auto editor = new PointFEdit(parent); + auto syncEditor = [property, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toPointF()); }; syncEditor(); QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(xSpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); - QObject::connect(ySpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); + QObject::connect(editor, &PointFEdit::valueChanged, property, + [property, editor] { + property->setValue(editor->value()); + }); return editor; } }; - class SizeEditorFactory : public EditorFactory { public: @@ -290,75 +238,45 @@ class SizeEditorFactory : public EditorFactory } }; -// todo: implement responsive layout (see SizeEdit) -class RectFEditorFactory : public EditorFactory +class SizeFEditorFactory : public EditorFactory { public: QWidget *createEditor(Property *property, QWidget *parent) override { - auto editor = new QWidget(parent); - auto gridLayout = new QGridLayout(editor); - gridLayout->setContentsMargins(QMargins()); - gridLayout->setColumnStretch(4, 1); - - auto xLabel = new QLabel(QStringLiteral("X"), editor); - gridLayout->addWidget(xLabel, 0, 0, Qt::AlignRight); - - auto xSpinBox = new DoubleSpinBox(editor); - xLabel->setBuddy(xSpinBox); - gridLayout->addWidget(xSpinBox, 0, 1); - - auto yLabel = new QLabel(QStringLiteral("Y"), editor); - gridLayout->addWidget(yLabel, 0, 2, Qt::AlignRight); - - auto ySpinBox = new DoubleSpinBox(editor); - yLabel->setBuddy(ySpinBox); - gridLayout->addWidget(ySpinBox, 0, 3); - - auto widthLabel = new QLabel(QStringLiteral("W"), editor); - widthLabel->setToolTip(tr("Width")); - gridLayout->addWidget(widthLabel, 1, 0, Qt::AlignRight); - - auto widthSpinBox = new DoubleSpinBox(editor); - widthLabel->setBuddy(widthSpinBox); - gridLayout->addWidget(widthSpinBox, 1, 1); + auto editor = new SizeFEdit(parent); + auto syncEditor = [property, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toSizeF()); + }; + syncEditor(); - auto heightLabel = new QLabel(QStringLiteral("H"), editor); - heightLabel->setToolTip(tr("Height")); - gridLayout->addWidget(heightLabel, 1, 2, Qt::AlignRight); + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &SizeFEdit::valueChanged, property, + [property, editor] { + property->setValue(editor->value()); + }); - auto heightSpinBox = new DoubleSpinBox(editor); - heightLabel->setBuddy(heightSpinBox); - gridLayout->addWidget(heightSpinBox, 1, 3); + return editor; + } +}; - auto syncEditor = [=] { - const QSignalBlocker xBlocker(xSpinBox); - const QSignalBlocker yBlocker(ySpinBox); - const QSignalBlocker widthBlocker(widthSpinBox); - const QSignalBlocker heightBlocker(heightSpinBox); - const auto rect = property->value().toRectF(); - xSpinBox->setValue(rect.x()); - ySpinBox->setValue(rect.y()); - widthSpinBox->setValue(rect.width()); - heightSpinBox->setValue(rect.height()); - }; - auto syncProperty = [=] { - property->setValue(QRectF(xSpinBox->value(), - ySpinBox->value(), - widthSpinBox->value(), - heightSpinBox->value())); +class RectFEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(Property *property, QWidget *parent) override + { + auto editor = new RectFEdit(parent); + auto syncEditor = [property, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toRectF()); }; syncEditor(); QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(xSpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); - QObject::connect(ySpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); - QObject::connect(widthSpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); - QObject::connect(heightSpinBox, qOverload(&DoubleSpinBox::valueChanged), - property, syncProperty); + QObject::connect(editor, &RectFEdit::valueChanged, property, + [property, editor] { + property->setValue(editor->value()); + }); return editor; } @@ -387,6 +305,132 @@ class ColorEditorFactory : public EditorFactory } }; +class FontEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(Property *property, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto layout = new QVBoxLayout(editor); + auto fontComboBox = new QFontComboBox(editor); + auto sizeSpinBox = new QSpinBox(editor); + auto boldCheckBox = new QCheckBox(tr("Bold"), editor); + auto italicCheckBox = new QCheckBox(tr("Italic"), editor); + auto underlineCheckBox = new QCheckBox(tr("Underline"), editor); + auto strikeoutCheckBox = new QCheckBox(tr("Strikeout"), editor); + auto kerningCheckBox = new QCheckBox(tr("Kerning"), editor); + sizeSpinBox->setRange(1, 999); + sizeSpinBox->setSuffix(tr(" px")); + sizeSpinBox->setKeyboardTracking(false); + layout->setContentsMargins(QMargins()); + layout->setSpacing(Utils::dpiScaled(3)); + layout->addWidget(fontComboBox); + layout->addWidget(sizeSpinBox); + layout->addWidget(boldCheckBox); + layout->addWidget(italicCheckBox); + layout->addWidget(underlineCheckBox); + layout->addWidget(strikeoutCheckBox); + layout->addWidget(kerningCheckBox); + + auto syncEditor = [=] { + const auto font = property->value().value(); + const QSignalBlocker fontBlocker(fontComboBox); + const QSignalBlocker sizeBlocker(sizeSpinBox); + const QSignalBlocker boldBlocker(boldCheckBox); + const QSignalBlocker italicBlocker(italicCheckBox); + const QSignalBlocker underlineBlocker(underlineCheckBox); + const QSignalBlocker strikeoutBlocker(strikeoutCheckBox); + const QSignalBlocker kerningBlocker(kerningCheckBox); + fontComboBox->setCurrentFont(font); + sizeSpinBox->setValue(font.pixelSize()); + boldCheckBox->setChecked(font.bold()); + italicCheckBox->setChecked(font.italic()); + underlineCheckBox->setChecked(font.underline()); + strikeoutCheckBox->setChecked(font.strikeOut()); + kerningCheckBox->setChecked(font.kerning()); + }; + + auto syncProperty = [=] { + auto font = fontComboBox->currentFont(); + font.setPixelSize(sizeSpinBox->value()); + font.setBold(boldCheckBox->isChecked()); + font.setItalic(italicCheckBox->isChecked()); + font.setUnderline(underlineCheckBox->isChecked()); + font.setStrikeOut(strikeoutCheckBox->isChecked()); + font.setKerning(kerningCheckBox->isChecked()); + property->setValue(font); + }; + + syncEditor(); + + QObject::connect(property, &Property::valueChanged, fontComboBox, syncEditor); + QObject::connect(fontComboBox, &QFontComboBox::currentFontChanged, property, syncProperty); + QObject::connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), property, syncProperty); + QObject::connect(boldCheckBox, &QCheckBox::toggled, property, syncProperty); + QObject::connect(italicCheckBox, &QCheckBox::toggled, property, syncProperty); + QObject::connect(underlineCheckBox, &QCheckBox::toggled, property, syncProperty); + QObject::connect(strikeoutCheckBox, &QCheckBox::toggled, property, syncProperty); + QObject::connect(kerningCheckBox, &QCheckBox::toggled, property, syncProperty); + + return editor; + } +}; + +class AlignmentEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(Property *property, QWidget *parent) override + { + auto editor = new QWidget(parent); + auto layout = new QGridLayout(editor); + layout->setContentsMargins(QMargins()); + layout->setSpacing(Utils::dpiScaled(3)); + + auto horizontalLabel = new ElidingLabel(tr("Horizontal"), editor); + layout->addWidget(horizontalLabel, 0, 0); + + auto verticalLabel = new ElidingLabel(tr("Vertical"), editor); + layout->addWidget(verticalLabel, 1, 0); + + auto horizontalComboBox = new QComboBox(editor); + horizontalComboBox->addItem(tr("Left"), Qt::AlignLeft); + horizontalComboBox->addItem(tr("Center"), Qt::AlignHCenter); + horizontalComboBox->addItem(tr("Right"), Qt::AlignRight); + horizontalComboBox->addItem(tr("Justify"), Qt::AlignJustify); + layout->addWidget(horizontalComboBox, 0, 1); + + auto verticalComboBox = new QComboBox(editor); + verticalComboBox->addItem(tr("Top"), Qt::AlignTop); + verticalComboBox->addItem(tr("Center"), Qt::AlignVCenter); + verticalComboBox->addItem(tr("Bottom"), Qt::AlignBottom); + layout->addWidget(verticalComboBox, 1, 1); + + layout->setColumnStretch(1, 1); + + auto syncEditor = [=] { + const QSignalBlocker horizontalBlocker(horizontalComboBox); + const QSignalBlocker verticalBlocker(verticalComboBox); + const auto alignment = property->value().value(); + horizontalComboBox->setCurrentIndex(horizontalComboBox->findData(static_cast(alignment & Qt::AlignHorizontal_Mask))); + verticalComboBox->setCurrentIndex(verticalComboBox->findData(static_cast(alignment & Qt::AlignVertical_Mask))); + }; + + auto syncProperty = [=] { + const Qt::Alignment alignment(horizontalComboBox->currentData().toInt() | + verticalComboBox->currentData().toInt()); + property->setValue(QVariant::fromValue(alignment)); + }; + + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(horizontalComboBox, qOverload(&QComboBox::currentIndexChanged), property, syncProperty); + QObject::connect(verticalComboBox, qOverload(&QComboBox::currentIndexChanged), property, syncProperty); + + return editor; + } +}; + ValueProperty::ValueProperty(const QString &name, const QVariant &value, @@ -425,7 +469,7 @@ VariantEditor::VariantEditor(QWidget *parent) : QScrollArea(parent) { m_widget = new QWidget; - m_widget->setBackgroundRole(QPalette::Base); + m_widget->setBackgroundRole(QPalette::AlternateBase); auto verticalLayout = new QVBoxLayout(m_widget); m_gridLayout = new QGridLayout; verticalLayout->addLayout(m_gridLayout); @@ -555,7 +599,6 @@ QWidget *VariantEditor::createEditor(Property *property) } - EnumEditorFactory::EnumEditorFactory(const QStringList &enumNames, const QList &enumValues) : m_enumNamesModel(enumNames) @@ -605,12 +648,15 @@ ValueTypeEditorFactory::ValueTypeEditorFactory() registerEditorFactory(QMetaType::Double, std::make_unique()); registerEditorFactory(QMetaType::Int, std::make_unique()); registerEditorFactory(QMetaType::QColor, std::make_unique()); + registerEditorFactory(QMetaType::QFont, std::make_unique()); registerEditorFactory(QMetaType::QPoint, std::make_unique()); registerEditorFactory(QMetaType::QPointF, std::make_unique()); registerEditorFactory(QMetaType::QRectF, std::make_unique()); registerEditorFactory(QMetaType::QSize, std::make_unique()); + registerEditorFactory(QMetaType::QSizeF, std::make_unique()); registerEditorFactory(QMetaType::QString, std::make_unique()); registerEditorFactory(QMetaType::QUrl, std::make_unique()); + registerEditorFactory(qMetaTypeId(), std::make_unique()); } void ValueTypeEditorFactory::registerEditorFactory(int type, std::unique_ptr factory) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 8099181347..3b282cb3a1 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -89,6 +89,21 @@ class EditorFactory virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; }; +struct IntEditorFactory : EditorFactory +{ + QWidget *createEditor(Property *property, QWidget *parent) override; +}; + +struct FloatEditorFactory : EditorFactory +{ + QWidget *createEditor(Property *property, QWidget *parent) override; + + void setSuffix(const QString &suffix) { m_suffix = suffix; } + +private: + QString m_suffix; +}; + /** * An editor factory that creates a combo box for enum properties. */ From 7a6de733cbf87e1d393ff28448dd924ef64d12cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 6 Sep 2024 13:28:58 +0200 Subject: [PATCH 13/78] Implemented Tile, WangSet and WangColor properties Now all built-in properties are present, apart from some loose ends. * Added RectEdit / RectEditorFactory (QRect values) * Added Property::toolTip (set on label and editor, but currently not functional on label due to the eliding tool tip logic) * Added change events to fix updating of WangSet and WangColor properties (was already broken with old framework) --- src/libtiled/wangset.h | 3 +- src/tiled/changeevents.h | 32 ++- src/tiled/colorbutton.cpp | 1 + src/tiled/propertieswidget.cpp | 348 +++++++++++++++++++++++++++- src/tiled/propertyeditorwidgets.cpp | 41 ++++ src/tiled/propertyeditorwidgets.h | 28 +++ src/tiled/tilesetview.cpp | 2 +- src/tiled/tilesetwangsetmodel.cpp | 18 +- src/tiled/tilesetwangsetmodel.h | 1 + src/tiled/varianteditor.cpp | 34 +++ src/tiled/varianteditor.h | 16 +- src/tiled/wangcolormodel.cpp | 17 ++ src/tiled/wangcolormodel.h | 2 + src/tiled/wangdock.cpp | 2 +- 14 files changed, 533 insertions(+), 12 deletions(-) diff --git a/src/libtiled/wangset.h b/src/libtiled/wangset.h index 2955762c0a..c2df394b53 100644 --- a/src/libtiled/wangset.h +++ b/src/libtiled/wangset.h @@ -424,5 +424,6 @@ TILEDSHARED_EXPORT WangSet::Type wangSetTypeFromString(const QString &); } // namespace Tiled -Q_DECLARE_METATYPE(Tiled::WangSet*) Q_DECLARE_METATYPE(Tiled::WangId) +Q_DECLARE_METATYPE(Tiled::WangSet*) +Q_DECLARE_METATYPE(Tiled::WangSet::Type) diff --git a/src/tiled/changeevents.h b/src/tiled/changeevents.h index 0dbf599280..313bb36dfa 100644 --- a/src/tiled/changeevents.h +++ b/src/tiled/changeevents.h @@ -61,6 +61,7 @@ class ChangeEvent WangSetRemoved, WangSetChanged, WangColorAboutToBeRemoved, + WangColorChanged, } type; protected: @@ -277,17 +278,20 @@ class WangSetChangeEvent : public ChangeEvent { public: enum WangSetProperty { - TypeProperty = 1 << 0, + NameProperty, + TypeProperty, + ImageProperty, + ColorCountProperty, }; - WangSetChangeEvent(WangSet *wangSet, int properties) + WangSetChangeEvent(WangSet *wangSet, WangSetProperty property) : ChangeEvent(WangSetChanged) , wangSet(wangSet) - , properties(properties) + , property(property) {} WangSet *wangSet; - int properties; + WangSetProperty property; }; class WangColorEvent : public ChangeEvent @@ -303,4 +307,24 @@ class WangColorEvent : public ChangeEvent int color; }; +class WangColorChangeEvent : public ChangeEvent +{ +public: + enum WangColorProperty { + NameProperty, + ColorProperty, + ImageProperty, + ProbabilityProperty, + }; + + WangColorChangeEvent(WangColor *wangColor, WangColorProperty property) + : ChangeEvent(WangColorChanged) + , wangColor(wangColor) + , property(property) + {} + + WangColor *wangColor; + WangColorProperty property; +}; + } // namespace Tiled diff --git a/src/tiled/colorbutton.cpp b/src/tiled/colorbutton.cpp index 85d39307c9..33f87c5406 100644 --- a/src/tiled/colorbutton.cpp +++ b/src/tiled/colorbutton.cpp @@ -75,6 +75,7 @@ void ColorButton::pickColor() void ColorButton::updateIcon() { + // todo: fix gray icon in disabled state (consider using opacity, and not using an icon at all) setIcon(Utils::colorIcon(mColor, iconSize())); } diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index d1f741ab69..bc416685b8 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -28,6 +28,10 @@ #include "changemapproperty.h" #include "changeobjectgroupproperties.h" #include "changeproperties.h" +#include "changetile.h" +#include "changetileimagesource.h" +#include "changewangcolordata.h" +#include "changewangsetdata.h" #include "clipboardmanager.h" #include "compression.h" #include "mapdocument.h" @@ -39,6 +43,7 @@ #include "tilesetdocument.h" #include "utils.h" #include "varianteditor.h" +#include "wangoverlay.h" #include #include @@ -1339,6 +1344,317 @@ class MapObjectProperties : public ObjectProperties std::unique_ptr mDegreesEditorFactory; }; +class TileProperties : public ObjectProperties +{ + Q_OBJECT + +public: + TileProperties(Document *document, Tile *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : ObjectProperties(document, object, parent) + { + mIdProperty = editorFactory->createProperty( + tr("ID"), + [this]() { return tile()->id(); }, + [](const QVariant &) {}); + mIdProperty->setEnabled(false); + + // todo: apply readableImageFormatsFilter + mImageProperty = editorFactory->createProperty( + tr("Image"), + [this]() { return tile()->imageSource(); }, + [this](const QVariant &value) { + push(new ChangeTileImageSource(tilesetDocument(), + tile(), + value.toUrl())); + }); + + mRectangleProperty = editorFactory->createProperty( + tr("Rectangle"), + [this]() { return tile()->imageRect(); }, + [this](const QVariant &value) { + push(new ChangeTileImageRect(tilesetDocument(), + { tile() }, + { value.toRect() })); + }); + + // todo: minimum value should be 0 + mProbabilityProperty = editorFactory->createProperty( + tr("Probability"), + [this]() { return tile()->probability(); }, + [this](const QVariant &value) { + push(new ChangeTileProbability(tilesetDocument(), + tilesetDocument()->selectedTiles(), + value.toReal())); + }); + mProbabilityProperty->setToolTip(tr("Relative chance this tile will be picked")); + + // annoying... maybe we should somehow always have the relevant TilesetDocument + if (auto tilesetDocument = qobject_cast(document)) { + connect(tilesetDocument, &TilesetDocument::tileImageSourceChanged, + this, &TileProperties::tileImageSourceChanged); + + connect(tilesetDocument, &TilesetDocument::tileProbabilityChanged, + this, &TileProperties::tileProbabilityChanged); + } else if (auto mapDocument = qobject_cast(document)) { + connect(mapDocument, &MapDocument::tileImageSourceChanged, + this, &TileProperties::tileImageSourceChanged); + + connect(mapDocument, &MapDocument::tileProbabilityChanged, + this, &TileProperties::tileProbabilityChanged); + } + + updateEnabledState(); + } + + void populateEditor(VariantEditor *editor) override + { + editor->addHeader(tr("Tile")); + editor->addProperty(mIdProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + + if (!tile()->imageSource().isEmpty()) + editor->addProperty(mImageProperty); + + editor->addProperty(mRectangleProperty); + editor->addProperty(mProbabilityProperty); + } + +private: + void tileImageSourceChanged(Tile *tile) + { + if (tile != this->tile()) + return; + emit mImageProperty->valueChanged(); + emit mRectangleProperty->valueChanged(); + } + + void tileProbabilityChanged(Tile *tile) + { + if (tile != this->tile()) + return; + emit mProbabilityProperty->valueChanged(); + } + + void updateEnabledState() + { + const bool hasTilesetDocument = tilesetDocument(); + const auto isCollection = tile()->tileset()->isCollection(); + mClassProperty->setEnabled(hasTilesetDocument); + mImageProperty->setEnabled(hasTilesetDocument && isCollection); + mRectangleProperty->setEnabled(hasTilesetDocument && isCollection); + mProbabilityProperty->setEnabled(hasTilesetDocument); + } + + TilesetDocument *tilesetDocument() const + { + return qobject_cast(mDocument); + } + + Tile *tile() const + { + return static_cast(mObject); + } + + Property *mIdProperty; + Property *mImageProperty; + Property *mRectangleProperty; + Property *mProbabilityProperty; +}; + +class WangSetProperties : public ObjectProperties +{ + Q_OBJECT + +public: + WangSetProperties(Document *document, WangSet *object, + ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : ObjectProperties(document, object, parent) + { + mNameProperty = editorFactory->createProperty( + tr("Name"), + [this]() { return wangSet()->name(); }, + [this](const QVariant &value) { + push(new RenameWangSet(tilesetDocument(), wangSet(), value.toString())); + }); + + mTypeProperty = editorFactory->createProperty( + tr("Type"), + [this]() { return QVariant::fromValue(wangSet()->type()); }, + [this](const QVariant &value) { + push(new ChangeWangSetType(tilesetDocument(), wangSet(), static_cast(value.toInt()))); + }); + + // todo: keep between 0 and WangId::MAX_COLOR_COUNT + mColorCountProperty = editorFactory->createProperty( + tr("Color Count"), + [this]() { return wangSet()->colorCount(); }, + [this](const QVariant &value) { + push(new ChangeWangSetColorCount(tilesetDocument(), + wangSet(), + value.toInt())); + }); + + connect(document, &Document::changed, + this, &WangSetProperties::onChanged); + + updateEnabledState(); + } + + void populateEditor(VariantEditor *editor) override + { + editor->addHeader(tr("Terrain Set")); + editor->addProperty(mNameProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + editor->addProperty(mTypeProperty); + editor->addProperty(mColorCountProperty); + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::WangSetChanged) + return; + + const auto &wangSetChange = static_cast(event); + if (wangSetChange.wangSet != wangSet()) + return; + + switch (wangSetChange.property) { + case WangSetChangeEvent::NameProperty: + emit mNameProperty->valueChanged(); + break; + case WangSetChangeEvent::TypeProperty: + emit mTypeProperty->valueChanged(); + break; + case WangSetChangeEvent::ImageProperty: + break; + case WangSetChangeEvent::ColorCountProperty: + emit mColorCountProperty->valueChanged(); + break; + } + } + + void updateEnabledState() + { + const bool hasTilesetDocument = tilesetDocument(); + mNameProperty->setEnabled(hasTilesetDocument); + mTypeProperty->setEnabled(hasTilesetDocument); + mColorCountProperty->setEnabled(hasTilesetDocument); + } + + TilesetDocument *tilesetDocument() const + { + return qobject_cast(mDocument); + } + + WangSet *wangSet() + { + return static_cast(mObject); + } + + Property *mNameProperty; + Property *mTypeProperty; + Property *mColorCountProperty; +}; + +class WangColorProperties : public ObjectProperties +{ + Q_OBJECT + +public: + WangColorProperties(Document *document, WangColor *object, + ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + : ObjectProperties(document, object, parent) + { + mNameProperty = editorFactory->createProperty( + tr("Name"), + [this]() { return wangColor()->name(); }, + [this](const QVariant &value) { + push(new ChangeWangColorName(tilesetDocument(), wangColor(), value.toString())); + }); + + mColorProperty = editorFactory->createProperty( + tr("Color"), + [this]() { return wangColor()->color(); }, + [this](const QVariant &value) { + push(new ChangeWangColorColor(tilesetDocument(), wangColor(), value.value())); + }); + + // todo: set 0.01 as minimum + mProbabilityProperty = editorFactory->createProperty( + tr("Probability"), + [this]() { return wangColor()->probability(); }, + [this](const QVariant &value) { + push(new ChangeWangColorProbability(tilesetDocument(), wangColor(), value.toReal())); + }); + + connect(document, &Document::changed, + this, &WangColorProperties::onChanged); + + updateEnabledState(); + } + + void populateEditor(VariantEditor *editor) override + { + editor->addHeader(tr("Terrain")); + editor->addProperty(mNameProperty); + editor->addProperty(mClassProperty); + editor->addSeparator(); + editor->addProperty(mColorProperty); + editor->addProperty(mProbabilityProperty); + } + +private: + void onChanged(const ChangeEvent &event) + { + if (event.type != ChangeEvent::WangColorChanged) + return; + + const auto &wangColorChange = static_cast(event); + if (wangColorChange.wangColor != wangColor()) + return; + + switch (wangColorChange.property) { + case WangColorChangeEvent::NameProperty: + emit mNameProperty->valueChanged(); + break; + case WangColorChangeEvent::ColorProperty: + emit mColorProperty->valueChanged(); + break; + case WangColorChangeEvent::ImageProperty: + break; + case WangColorChangeEvent::ProbabilityProperty: + emit mProbabilityProperty->valueChanged(); + break; + } + } + + void updateEnabledState() + { + const bool hasTilesetDocument = tilesetDocument(); + mNameProperty->setEnabled(hasTilesetDocument); + mClassProperty->setEnabled(hasTilesetDocument); + mColorProperty->setEnabled(hasTilesetDocument); + mProbabilityProperty->setEnabled(hasTilesetDocument); + } + + TilesetDocument *tilesetDocument() const + { + return qobject_cast(mDocument); + } + + WangColor *wangColor() + { + return static_cast(mObject); + } + + Property *mNameProperty; + Property *mColorProperty; + Property *mProbabilityProperty; +}; + void PropertiesWidget::currentObjectChanged(Object *object) { @@ -1385,13 +1701,23 @@ void PropertiesWidget::currentObjectChanged(Object *object) mDefaultEditorFactory.get(), this); break; case Object::TileType: - // todo + mPropertiesObject = new TileProperties(mDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); + break; case Object::WangSetType: - // todo + mPropertiesObject = new WangSetProperties(mDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); + break; case Object::WangColorType: - // todo + mPropertiesObject = new WangColorProperties(mDocument, + static_cast(object), + mDefaultEditorFactory.get(), this); + break; case Object::ProjectType: case Object::WorldType: + // these types are currently not handled by the Properties dock break; } } @@ -1886,6 +2212,22 @@ void PropertiesWidget::registerEditorFactories() tr("Top Down"), tr("Index Order"), })); + + auto wangSetTypeEditorFactory = std::make_unique( + QStringList { + tr("Corner"), + tr("Edge"), + tr("Mixed"), + }); + + QMap mWangSetIcons; + mWangSetIcons.insert(WangSet::Corner, wangSetIcon(WangSet::Corner)); + mWangSetIcons.insert(WangSet::Edge, wangSetIcon(WangSet::Edge)); + mWangSetIcons.insert(WangSet::Mixed, wangSetIcon(WangSet::Mixed)); + wangSetTypeEditorFactory->setEnumIcons(mWangSetIcons); + + registerEditorFactory(qMetaTypeId(), + std::move(wangSetTypeEditorFactory)); } void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 3bb86cb855..848652b8d4 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -302,6 +302,47 @@ QPointF PointFEdit::value() const } +RectEdit::RectEdit(QWidget *parent) + : ResponsivePairswiseWidget(parent) + , m_xLabel(new QLabel(QStringLiteral("X"), this)) + , m_yLabel(new QLabel(QStringLiteral("Y"), this)) + , m_widthLabel(new QLabel(QStringLiteral("W"), this)) + , m_heightLabel(new QLabel(QStringLiteral("H"), this)) + , m_xSpinBox(new SpinBox(this)) + , m_ySpinBox(new SpinBox(this)) + , m_widthSpinBox(new SpinBox(this)) + , m_heightSpinBox(new SpinBox(this)) +{ + setWidgetPairs({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }); + + connect(m_xSpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); + connect(m_ySpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); + connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); + connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); +} + +void RectEdit::setValue(const QRect &rect) +{ + m_xSpinBox->setValue(rect.x()); + m_ySpinBox->setValue(rect.y()); + m_widthSpinBox->setValue(rect.width()); + m_heightSpinBox->setValue(rect.height()); +} + +QRect RectEdit::value() const +{ + return QRect(m_xSpinBox->value(), + m_ySpinBox->value(), + m_widthSpinBox->value(), + m_heightSpinBox->value()); +} + + RectFEdit::RectFEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) , m_xLabel(new QLabel(QStringLiteral("X"), this)) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 342b0b505a..dc8c9417f0 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -166,6 +166,34 @@ class PointFEdit : public ResponsivePairswiseWidget DoubleSpinBox *m_ySpinBox; }; +/** + * A widget for editing a QRect value. + */ +class RectEdit : public ResponsivePairswiseWidget +{ + Q_OBJECT + Q_PROPERTY(QRect value READ value WRITE setValue NOTIFY valueChanged FINAL) + +public: + RectEdit(QWidget *parent = nullptr); + + void setValue(const QRect &size); + QRect value() const; + +signals: + void valueChanged(); + +private: + QLabel *m_xLabel; + QLabel *m_yLabel; + QLabel *m_widthLabel; + QLabel *m_heightLabel; + SpinBox *m_xSpinBox; + SpinBox *m_ySpinBox; + SpinBox *m_widthSpinBox; + SpinBox *m_heightSpinBox; +}; + /** * A widget for editing a QRectF value. */ diff --git a/src/tiled/tilesetview.cpp b/src/tiled/tilesetview.cpp index 9693a45d6a..ac15fce3c2 100644 --- a/src/tiled/tilesetview.cpp +++ b/src/tiled/tilesetview.cpp @@ -831,7 +831,7 @@ void TilesetView::onChange(const ChangeEvent &change) case ChangeEvent::WangSetChanged: { auto &wangSetChange = static_cast(change); if (mEditWangSet && wangSetChange.wangSet == mWangSet && - (wangSetChange.properties & WangSetChangeEvent::TypeProperty)) { + (wangSetChange.property == WangSetChangeEvent::TypeProperty)) { viewport()->update(); } break; diff --git a/src/tiled/tilesetwangsetmodel.cpp b/src/tiled/tilesetwangsetmodel.cpp index 5a6d6f5a6d..76ccbf1a8a 100644 --- a/src/tiled/tilesetwangsetmodel.cpp +++ b/src/tiled/tilesetwangsetmodel.cpp @@ -22,6 +22,7 @@ #include "changeevents.h" #include "changewangsetdata.h" +#include "mapdocument.h" #include "tile.h" #include "tileset.h" #include "tilesetdocument.h" @@ -159,13 +160,14 @@ void TilesetWangSetModel::setWangSetName(WangSet *wangSet, const QString &name) Q_ASSERT(wangSet->tileset() == mTilesetDocument->tileset().data()); wangSet->setName(name); emitWangSetChange(wangSet); + emitToTilesetAndMaps(WangSetChangeEvent(wangSet, WangSetChangeEvent::NameProperty)); } void TilesetWangSetModel::setWangSetType(WangSet *wangSet, WangSet::Type type) { Q_ASSERT(wangSet->tileset() == mTilesetDocument->tileset().data()); wangSet->setType(type); - emit mTilesetDocument->changed(WangSetChangeEvent(wangSet, WangSetChangeEvent::TypeProperty)); + emitToTilesetAndMaps(WangSetChangeEvent(wangSet, WangSetChangeEvent::TypeProperty)); } void TilesetWangSetModel::setWangSetColorCount(WangSet *wangSet, int value) @@ -173,6 +175,7 @@ void TilesetWangSetModel::setWangSetColorCount(WangSet *wangSet, int value) Q_ASSERT(wangSet->tileset() == mTilesetDocument->tileset().data()); wangSet->setColorCount(value); emitWangSetChange(wangSet); + emitToTilesetAndMaps(WangSetChangeEvent(wangSet, WangSetChangeEvent::ColorCountProperty)); } void TilesetWangSetModel::setWangSetImage(WangSet *wangSet, int tileId) @@ -187,6 +190,7 @@ void TilesetWangSetModel::insertWangColor(WangSet *wangSet, const QSharedPointer Q_ASSERT(wangSet->tileset() == mTilesetDocument->tileset().data()); wangSet->insertWangColor(wangColor); emitWangSetChange(wangSet); + emitToTilesetAndMaps(WangSetChangeEvent(wangSet, WangSetChangeEvent::ColorCountProperty)); } QSharedPointer TilesetWangSetModel::takeWangColorAt(WangSet *wangSet, int color) @@ -196,6 +200,7 @@ QSharedPointer TilesetWangSetModel::takeWangColorAt(WangSet *wangSet, auto wangColor = wangSet->takeWangColorAt(color); emit wangColorRemoved(wangColor.data()); emitWangSetChange(wangSet); + emitToTilesetAndMaps(WangSetChangeEvent(wangSet, WangSetChangeEvent::ColorCountProperty)); return wangColor; } @@ -206,6 +211,17 @@ void TilesetWangSetModel::emitWangSetChange(WangSet *wangSet) emit wangSetChanged(wangSet); } +void TilesetWangSetModel::emitToTilesetAndMaps(const ChangeEvent &event) +{ + emit mTilesetDocument->changed(event); + + // todo: this doesn't work reliably because it only reaches maps that use + // the tileset, whereas the Properties view can be showing stuff from any + // tileset. + for (MapDocument *mapDocument : mTilesetDocument->mapDocuments()) + emit mapDocument->changed(event); +} + void TilesetWangSetModel::documentChanged(const ChangeEvent &event) { switch (event.type) { diff --git a/src/tiled/tilesetwangsetmodel.h b/src/tiled/tilesetwangsetmodel.h index 3a61c2acd4..6675acb3be 100644 --- a/src/tiled/tilesetwangsetmodel.h +++ b/src/tiled/tilesetwangsetmodel.h @@ -98,6 +98,7 @@ class TilesetWangSetModel : public QAbstractListModel void documentChanged(const ChangeEvent &event); void emitWangSetChange(WangSet *wangSet); + void emitToTilesetAndMaps(const ChangeEvent &event); TilesetDocument *mTilesetDocument; }; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 66ef71b144..917a71d2eb 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -260,6 +260,28 @@ class SizeFEditorFactory : public EditorFactory } }; +class RectEditorFactory : public EditorFactory +{ +public: + QWidget *createEditor(Property *property, QWidget *parent) override + { + auto editor = new RectEdit(parent); + auto syncEditor = [property, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(property->value().toRect()); + }; + syncEditor(); + + QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &RectEdit::valueChanged, property, + [property, editor] { + property->setValue(editor->value()); + }); + + return editor; + } +}; + class RectFEditorFactory : public EditorFactory { public: @@ -546,15 +568,20 @@ void VariantEditor::addProperty(Property *property) { auto label = new LineEditLabel(property->name(), m_widget); label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + label->setToolTip(property->toolTip()); label->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); connect(property, &Property::enabledChanged, label, &QLabel::setEnabled); m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); if (auto editor = createEditor(property)) { + editor->setToolTip(property->toolTip()); editor->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); } + ++m_rowIndex; } @@ -610,6 +637,12 @@ void EnumEditorFactory::setEnumNames(const QStringList &enumNames) m_enumNamesModel.setStringList(enumNames); } +void EnumEditorFactory::setEnumIcons(const QMap &enumIcons) +{ + // todo: add support for showing these icons in the QComboBox + m_enumIcons = enumIcons; +} + void EnumEditorFactory::setEnumValues(const QList &enumValues) { m_enumValues = enumValues; @@ -651,6 +684,7 @@ ValueTypeEditorFactory::ValueTypeEditorFactory() registerEditorFactory(QMetaType::QFont, std::make_unique()); registerEditorFactory(QMetaType::QPoint, std::make_unique()); registerEditorFactory(QMetaType::QPointF, std::make_unique()); + registerEditorFactory(QMetaType::QRect, std::make_unique()); registerEditorFactory(QMetaType::QRectF, std::make_unique()); registerEditorFactory(QMetaType::QSize, std::make_unique()); registerEditorFactory(QMetaType::QSizeF, std::make_unique()); diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 3b282cb3a1..1e05876637 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -42,6 +42,7 @@ class Property : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) @@ -53,6 +54,15 @@ class Property : public QObject const QString &name() const { return m_name; } + const QString &toolTip() const { return m_toolTip; } + void setToolTip(const QString &toolTip) + { + if (m_toolTip != toolTip) { + m_toolTip = toolTip; + emit toolTipChanged(toolTip); + } + } + bool isEnabled() const { return m_enabled; } void setEnabled(bool enabled) { @@ -68,11 +78,13 @@ class Property : public QObject virtual QWidget *createEditor(QWidget *parent) = 0; signals: + void toolTipChanged(const QString &toolTip); void valueChanged(); void enabledChanged(bool enabled); private: QString m_name; + QString m_toolTip; bool m_enabled = true; }; @@ -114,12 +126,14 @@ class EnumEditorFactory : public EditorFactory const QList &enumValues = {}); void setEnumNames(const QStringList &enumNames); + void setEnumIcons(const QMap &enumIcons); void setEnumValues(const QList &enumValues); QWidget *createEditor(Property *property, QWidget *parent) override; private: QStringListModel m_enumNamesModel; + QMap m_enumIcons; QList m_enumValues; }; @@ -198,7 +212,7 @@ class ValueProperty : public AbstractProperty * * The property does not take ownership of the editor factory. */ -class QObjectProperty : public AbstractProperty +class QObjectProperty final : public AbstractProperty { Q_OBJECT diff --git a/src/tiled/wangcolormodel.cpp b/src/tiled/wangcolormodel.cpp index 28c177df27..65665cc088 100644 --- a/src/tiled/wangcolormodel.cpp +++ b/src/tiled/wangcolormodel.cpp @@ -20,7 +20,9 @@ #include "wangcolormodel.h" +#include "changeevents.h" #include "changewangcolordata.h" +#include "mapdocument.h" #include "tileset.h" #include "tilesetdocument.h" #include "wangset.h" @@ -130,24 +132,28 @@ void WangColorModel::setName(WangColor *wangColor, const QString &name) { wangColor->setName(name); emitDataChanged(wangColor); + emitToTilesetAndMaps(WangColorChangeEvent(wangColor, WangColorChangeEvent::NameProperty)); } void WangColorModel::setImage(WangColor *wangColor, int imageId) { wangColor->setImageId(imageId); emitDataChanged(wangColor); + emitToTilesetAndMaps(WangColorChangeEvent(wangColor, WangColorChangeEvent::ImageProperty)); } void WangColorModel::setColor(WangColor *wangColor, const QColor &color) { wangColor->setColor(color); emitDataChanged(wangColor); + emitToTilesetAndMaps(WangColorChangeEvent(wangColor, WangColorChangeEvent::ColorProperty)); } void WangColorModel::setProbability(WangColor *wangColor, qreal probability) { wangColor->setProbability(probability); // no data changed signal because probability not exposed by model + emitToTilesetAndMaps(WangColorChangeEvent(wangColor, WangColorChangeEvent::ProbabilityProperty)); } void WangColorModel::emitDataChanged(WangColor *wangColor) @@ -156,4 +162,15 @@ void WangColorModel::emitDataChanged(WangColor *wangColor) emit dataChanged(i, i); } +void WangColorModel::emitToTilesetAndMaps(const ChangeEvent &event) +{ + emit mTilesetDocument->changed(event); + + // todo: this doesn't work reliably because it only reaches maps that use + // the tileset, whereas the Properties view can be showing stuff from any + // tileset. + for (MapDocument *mapDocument : mTilesetDocument->mapDocuments()) + emit mapDocument->changed(event); +} + #include "moc_wangcolormodel.cpp" diff --git a/src/tiled/wangcolormodel.h b/src/tiled/wangcolormodel.h index f07de6a1bb..050f3c5fed 100644 --- a/src/tiled/wangcolormodel.h +++ b/src/tiled/wangcolormodel.h @@ -28,6 +28,7 @@ namespace Tiled { class Tileset; +class ChangeEvent; class TilesetDocument; class WangColorModel : public QAbstractListModel @@ -71,6 +72,7 @@ class WangColorModel : public QAbstractListModel private: void emitDataChanged(WangColor *wangColor); + void emitToTilesetAndMaps(const ChangeEvent &event); TilesetDocument *mTilesetDocument; WangSet *mWangSet; diff --git a/src/tiled/wangdock.cpp b/src/tiled/wangdock.cpp index 5ab91b6533..25db17baf5 100644 --- a/src/tiled/wangdock.cpp +++ b/src/tiled/wangdock.cpp @@ -484,7 +484,7 @@ void WangDock::documentChanged(const ChangeEvent &change) } break; case ChangeEvent::WangSetChanged: - if (static_cast(change).properties & WangSetChangeEvent::TypeProperty) + if (static_cast(change).property == WangSetChangeEvent::TypeProperty) mWangTemplateModel->wangSetChanged(); break; default: From d89f757a05c00e3ee6e5e48189300afd5ef46dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 6 Sep 2024 17:48:32 +0200 Subject: [PATCH 14/78] Refactor that gets rid of most editor factories Instead there are now typed Property subclasses similar to the generic GetSetProperty. --- src/tiled/propertieswidget.cpp | 133 +++---- src/tiled/varianteditor.cpp | 680 ++++++++++++++++----------------- src/tiled/varianteditor.h | 126 +++++- 3 files changed, 495 insertions(+), 444 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index bc416685b8..525d1fecbd 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -171,13 +171,22 @@ static QStringList classNamesFor(const Object &object) return names; } -class ClassProperty : public Property +// todo: add support for changing multiple objects +class ClassProperty : public StringProperty { Q_OBJECT public: ClassProperty(Document *document, Object *object, QObject *parent = nullptr) - : Property(tr("Class"), parent) + : StringProperty(tr("Class"), + [this] { return mObject->className(); }, + [this] (const QString &value) { + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->push(new ChangeClassName(mDocument, + { mObject }, + value)); + }, + parent) , mDocument(document) , mObject(object) { @@ -185,15 +194,6 @@ class ClassProperty : public Property this, &ClassProperty::onChanged); } - QVariant value() const override { return mObject->className(); } - void setValue(const QVariant &value) override - { - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new ChangeClassName(mDocument, - { mObject }, // todo: add support for changing multiple objects - value.toString())); - } - QWidget *createEditor(QWidget *parent) override { auto editor = new QComboBox(parent); @@ -232,28 +232,27 @@ class ClassProperty : public Property Object *mObject; }; -class MapSizeProperty : public AbstractProperty +class MapSizeProperty : public SizeProperty { Q_OBJECT public: - MapSizeProperty(MapDocument *mapDocument, EditorFactory *editorFactory, + MapSizeProperty(MapDocument *mapDocument, QObject *parent = nullptr) - : AbstractProperty(tr("Map Size"), editorFactory, parent) + : SizeProperty(tr("Map Size"), + [this]{ return mMapDocument->map()->size(); }, {}, + parent) , mMapDocument(mapDocument) { connect(mMapDocument, &MapDocument::mapChanged, this, &Property::valueChanged); } - QVariant value() const override { return mMapDocument->map()->size(); } - void setValue(const QVariant &) override {}; - QWidget *createEditor(QWidget *parent) override { auto widget = new QWidget(parent); auto layout = new QVBoxLayout(widget); - auto valueEdit = AbstractProperty::createEditor(widget); + auto valueEdit = SizeProperty::createEditor(widget); auto resizeButton = new QPushButton(tr("Resize Map"), widget); valueEdit->setEnabled(false); @@ -272,48 +271,6 @@ class MapSizeProperty : public AbstractProperty MapDocument *mMapDocument; }; -class TileSizeProperty : public AbstractProperty -{ - Q_OBJECT - -public: - TileSizeProperty(MapDocument *mapDocument, - EditorFactory *editorFactory, - QObject *parent = nullptr) - : AbstractProperty(tr("Tile Size"), editorFactory, parent) - , mMapDocument(mapDocument) - { - } - - QVariant value() const override - { - return mMapDocument->map()->tileSize(); - } - - void setValue(const QVariant &value) override - { - auto oldSize = mMapDocument->map()->tileSize(); - auto newSize = value.toSize(); - - if (oldSize.width() != newSize.width()) { - auto command = new ChangeMapProperty(mMapDocument, - Map::TileWidthProperty, - newSize.width()); - mMapDocument->undoStack()->push(command); - } - - if (oldSize.height() != newSize.height()) { - auto command = new ChangeMapProperty(mMapDocument, - Map::TileHeightProperty, - newSize.height()); - mMapDocument->undoStack()->push(command); - } - }; - -private: - MapDocument *mMapDocument; -}; - class ObjectProperties : public QObject { Q_OBJECT @@ -364,22 +321,42 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), orientation)); }); - mSizeProperty = new MapSizeProperty(mapDocument(), editorFactory, this); + mSizeProperty = new MapSizeProperty(mapDocument(), this); - mTileSizeProperty = new TileSizeProperty(mapDocument(), editorFactory, this); + mTileSizeProperty = new SizeProperty( + tr("Tile Size"), + [this] { + return mapDocument()->map()->tileSize(); + }, + [this](const QSize &newSize) { + const auto oldSize = mapDocument()->map()->tileSize(); + + if (oldSize.width() != newSize.width()) { + push(new ChangeMapProperty(mapDocument(), + Map::TileWidthProperty, + newSize.width())); + } + + if (oldSize.height() != newSize.height()) { + push(new ChangeMapProperty(mapDocument(), + Map::TileHeightProperty, + newSize.height())); + } + }, + this); - mInfiniteProperty = editorFactory->createProperty( + mInfiniteProperty = new BoolProperty( tr("Infinite"), [this]() { return map()->infinite(); }, - [this](const QVariant &value) { + [this](const bool &value) { push(new ChangeMapProperty(mapDocument(), Map::InfiniteProperty, - value.toInt())); + value ? 1 : 0)); }); - mHexSideLengthProperty = editorFactory->createProperty( + mHexSideLengthProperty = new IntProperty( tr("Hex Side Length"), [this]() { return map()->hexSideLength(); @@ -1083,24 +1060,19 @@ class MapObjectProperties : public ObjectProperties public: MapObjectProperties(MapDocument *document, MapObject *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) - , mDegreesEditorFactory(std::make_unique()) { - mDegreesEditorFactory->setSuffix(QStringLiteral("°")); - - mIdProperty = editorFactory->createProperty( + mIdProperty = new IntProperty( tr("ID"), - [this]() { return mapObject()->id(); }, - [](const QVariant &) {}); + [this]() { return mapObject()->id(); }); mIdProperty->setEnabled(false); - mTemplateProperty = editorFactory->createProperty( + mTemplateProperty = new UrlProperty( tr("Template"), [this]() { if (auto objectTemplate = mapObject()->objectTemplate()) return QUrl::fromLocalFile(objectTemplate->fileName()); return QUrl(); - }, - [](const QVariant &) {}); + }); mTemplateProperty->setEnabled(false); mNameProperty = editorFactory->createProperty( @@ -1139,15 +1111,16 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::SizeProperty, value); }); - mRotationProperty = new GetSetProperty( + mRotationProperty = new FloatProperty( tr("Rotation"), [this]() { return mapObject()->rotation(); }, - [this](const QVariant &value) { + [this](const qreal &value) { changeMapObject(MapObject::RotationProperty, value); }, - mDegreesEditorFactory.get(), this); + this); + mRotationProperty->setSuffix(QStringLiteral("°")); // todo: make this a custom widget with "Horizontal" and "Vertical" checkboxes mFlippingProperty = editorFactory->createProperty( @@ -1329,7 +1302,7 @@ class MapObjectProperties : public ObjectProperties Property *mVisibleProperty; Property *mPositionProperty; Property *mSizeProperty; - Property *mRotationProperty; + FloatProperty *mRotationProperty; // for tile objects Property *mFlippingProperty; @@ -1340,8 +1313,6 @@ class MapObjectProperties : public ObjectProperties Property *mTextFontProperty; Property *mTextWordWrapProperty; Property *mTextColorProperty; - - std::unique_ptr mDegreesEditorFactory; }; class TileProperties : public ObjectProperties diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 917a71d2eb..52ae9513a4 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -65,393 +65,334 @@ GetSetProperty::GetSetProperty(const QString &name, {} - -class StringEditorFactory : public EditorFactory +QWidget *StringProperty::createEditor(QWidget *parent) { -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new QLineEdit(parent); - auto syncEditor = [=] { - editor->setText(property->value().toString()); - }; - syncEditor(); + auto editor = new QLineEdit(parent); + auto syncEditor = [=] { + editor->setText(m_get()); + }; + syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &QLineEdit::textEdited, property, &Property::setValue); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &QLineEdit::textEdited, this, m_set); - return editor; - } -}; + return editor; +} -class UrlEditorFactory : public EditorFactory +QWidget *UrlProperty::createEditor(QWidget *parent) { -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new FileEdit(parent); - editor->setFilter(m_filter); - - auto syncEditor = [=] { - editor->setFileUrl(property->value().toUrl()); - }; - syncEditor(); + auto editor = new FileEdit(parent); + editor->setFilter(m_filter); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &FileEdit::fileUrlChanged, property, &Property::setValue); - - return editor; - } + auto syncEditor = [=] { + editor->setFileUrl(m_get()); + }; + syncEditor(); - void setFilter(const QString &filter) - { - m_filter = filter; - } + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &FileEdit::fileUrlChanged, this, m_set); -private: - QString m_filter; -}; + return editor; +} -QWidget *IntEditorFactory::createEditor(Property *property, QWidget *parent) +QWidget *IntProperty::createEditor(QWidget *parent) { auto editor = new SpinBox(parent); auto syncEditor = [=] { const QSignalBlocker blocker(editor); - editor->setValue(property->value().toInt()); + editor->setValue(m_get()); }; syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, qOverload(&SpinBox::valueChanged), - property, &Property::setValue); + this, m_set); return editor; } - -QWidget *FloatEditorFactory::createEditor(Property *property, QWidget *parent) +QWidget *FloatProperty::createEditor(QWidget *parent) { auto editor = new DoubleSpinBox(parent); editor->setSuffix(m_suffix); auto syncEditor = [=] { const QSignalBlocker blocker(editor); - editor->setValue(property->value().toDouble()); + editor->setValue(m_get()); }; syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), - property, &Property::setValue); + this, m_set); return editor; } +QWidget *BoolProperty::createEditor(QWidget *parent) +{ + auto editor = new QCheckBox(parent); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + bool checked = m_get(); + editor->setChecked(checked); + editor->setText(checked ? tr("On") : tr("Off")); + }; + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &QCheckBox::toggled, this, [=](bool checked) { + editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); + m_set(checked); + }); -class BoolEditorFactory : public EditorFactory + return editor; +} + +QWidget *PointProperty::createEditor(QWidget *parent) { -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new QCheckBox(parent); - auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - bool checked = property->value().toBool(); - editor->setChecked(checked); - editor->setText(checked ? tr("On") : tr("Off")); - }; - syncEditor(); + auto editor = new PointEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(m_get()); + }; + syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &QCheckBox::toggled, property, [=](bool checked) { - editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); - property->setValue(checked); - }); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &PointEdit::valueChanged, this, + [this, editor] { + m_set(editor->value()); + }); - return editor; - } -}; - -class PointEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new PointEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toPoint()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &PointEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); + return editor; +} - return editor; - } -}; - -class PointFEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new PointFEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toPointF()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &PointFEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); +QWidget *PointFProperty::createEditor(QWidget *parent) +{ + auto editor = new PointFEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(this->value().toPointF()); + }; + syncEditor(); - return editor; - } -}; - -class SizeEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new SizeEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toSize()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &SizeEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &PointFEdit::valueChanged, this, + [this, editor] { + this->setValue(editor->value()); + }); - return editor; - } -}; - -class SizeFEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new SizeFEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toSizeF()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &SizeFEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); + return editor; +} - return editor; - } -}; - -class RectEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new RectEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toRect()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &RectEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); +QWidget *SizeProperty::createEditor(QWidget *parent) +{ + auto editor = new SizeEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(m_get()); + }; + syncEditor(); - return editor; - } -}; - -class RectFEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new RectFEdit(parent); - auto syncEditor = [property, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(property->value().toRectF()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &RectFEdit::valueChanged, property, - [property, editor] { - property->setValue(editor->value()); - }); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &SizeEdit::valueChanged, this, + [this, editor] { + m_set(editor->value()); + }); - return editor; - } -}; + return editor; +} + +QWidget *SizeFProperty::createEditor(QWidget *parent) +{ + auto editor = new SizeFEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(this->value().toSizeF()); + }; + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &SizeFEdit::valueChanged, this, + [this, editor] { + this->setValue(editor->value()); + }); + + return editor; +} + +QWidget *RectProperty::createEditor(QWidget *parent) +{ + auto editor = new RectEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(this->value().toRect()); + }; + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &RectEdit::valueChanged, this, + [this, editor] { + this->setValue(editor->value()); + }); + + return editor; +} + +QWidget *RectFProperty::createEditor(QWidget *parent) +{ + auto editor = new RectFEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(this->value().toRectF()); + }; + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &RectFEdit::valueChanged, this, + [this, editor] { + this->setValue(editor->value()); + }); + + return editor; +} // todo: needs to handle invalid color (unset value) -class ColorEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new ColorButton(parent); - auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - editor->setColor(property->value().value()); - }; - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &ColorButton::colorChanged, property, - [property, editor] { - property->setValue(editor->color()); - }); +QWidget *ColorProperty::createEditor(QWidget *parent) +{ + auto editor = new ColorButton(parent); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setColor(this->value().value()); + }; + syncEditor(); - return editor; - } -}; - -class FontEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new QWidget(parent); - auto layout = new QVBoxLayout(editor); - auto fontComboBox = new QFontComboBox(editor); - auto sizeSpinBox = new QSpinBox(editor); - auto boldCheckBox = new QCheckBox(tr("Bold"), editor); - auto italicCheckBox = new QCheckBox(tr("Italic"), editor); - auto underlineCheckBox = new QCheckBox(tr("Underline"), editor); - auto strikeoutCheckBox = new QCheckBox(tr("Strikeout"), editor); - auto kerningCheckBox = new QCheckBox(tr("Kerning"), editor); - sizeSpinBox->setRange(1, 999); - sizeSpinBox->setSuffix(tr(" px")); - sizeSpinBox->setKeyboardTracking(false); - layout->setContentsMargins(QMargins()); - layout->setSpacing(Utils::dpiScaled(3)); - layout->addWidget(fontComboBox); - layout->addWidget(sizeSpinBox); - layout->addWidget(boldCheckBox); - layout->addWidget(italicCheckBox); - layout->addWidget(underlineCheckBox); - layout->addWidget(strikeoutCheckBox); - layout->addWidget(kerningCheckBox); - - auto syncEditor = [=] { - const auto font = property->value().value(); - const QSignalBlocker fontBlocker(fontComboBox); - const QSignalBlocker sizeBlocker(sizeSpinBox); - const QSignalBlocker boldBlocker(boldCheckBox); - const QSignalBlocker italicBlocker(italicCheckBox); - const QSignalBlocker underlineBlocker(underlineCheckBox); - const QSignalBlocker strikeoutBlocker(strikeoutCheckBox); - const QSignalBlocker kerningBlocker(kerningCheckBox); - fontComboBox->setCurrentFont(font); - sizeSpinBox->setValue(font.pixelSize()); - boldCheckBox->setChecked(font.bold()); - italicCheckBox->setChecked(font.italic()); - underlineCheckBox->setChecked(font.underline()); - strikeoutCheckBox->setChecked(font.strikeOut()); - kerningCheckBox->setChecked(font.kerning()); - }; - - auto syncProperty = [=] { - auto font = fontComboBox->currentFont(); - font.setPixelSize(sizeSpinBox->value()); - font.setBold(boldCheckBox->isChecked()); - font.setItalic(italicCheckBox->isChecked()); - font.setUnderline(underlineCheckBox->isChecked()); - font.setStrikeOut(strikeoutCheckBox->isChecked()); - font.setKerning(kerningCheckBox->isChecked()); - property->setValue(font); - }; - - syncEditor(); - - QObject::connect(property, &Property::valueChanged, fontComboBox, syncEditor); - QObject::connect(fontComboBox, &QFontComboBox::currentFontChanged, property, syncProperty); - QObject::connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), property, syncProperty); - QObject::connect(boldCheckBox, &QCheckBox::toggled, property, syncProperty); - QObject::connect(italicCheckBox, &QCheckBox::toggled, property, syncProperty); - QObject::connect(underlineCheckBox, &QCheckBox::toggled, property, syncProperty); - QObject::connect(strikeoutCheckBox, &QCheckBox::toggled, property, syncProperty); - QObject::connect(kerningCheckBox, &QCheckBox::toggled, property, syncProperty); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, &ColorButton::colorChanged, this, + [this, editor] { + this->setValue(editor->color()); + }); - return editor; - } -}; - -class AlignmentEditorFactory : public EditorFactory -{ -public: - QWidget *createEditor(Property *property, QWidget *parent) override - { - auto editor = new QWidget(parent); - auto layout = new QGridLayout(editor); - layout->setContentsMargins(QMargins()); - layout->setSpacing(Utils::dpiScaled(3)); - - auto horizontalLabel = new ElidingLabel(tr("Horizontal"), editor); - layout->addWidget(horizontalLabel, 0, 0); - - auto verticalLabel = new ElidingLabel(tr("Vertical"), editor); - layout->addWidget(verticalLabel, 1, 0); - - auto horizontalComboBox = new QComboBox(editor); - horizontalComboBox->addItem(tr("Left"), Qt::AlignLeft); - horizontalComboBox->addItem(tr("Center"), Qt::AlignHCenter); - horizontalComboBox->addItem(tr("Right"), Qt::AlignRight); - horizontalComboBox->addItem(tr("Justify"), Qt::AlignJustify); - layout->addWidget(horizontalComboBox, 0, 1); - - auto verticalComboBox = new QComboBox(editor); - verticalComboBox->addItem(tr("Top"), Qt::AlignTop); - verticalComboBox->addItem(tr("Center"), Qt::AlignVCenter); - verticalComboBox->addItem(tr("Bottom"), Qt::AlignBottom); - layout->addWidget(verticalComboBox, 1, 1); - - layout->setColumnStretch(1, 1); - - auto syncEditor = [=] { - const QSignalBlocker horizontalBlocker(horizontalComboBox); - const QSignalBlocker verticalBlocker(verticalComboBox); - const auto alignment = property->value().value(); - horizontalComboBox->setCurrentIndex(horizontalComboBox->findData(static_cast(alignment & Qt::AlignHorizontal_Mask))); - verticalComboBox->setCurrentIndex(verticalComboBox->findData(static_cast(alignment & Qt::AlignVertical_Mask))); - }; - - auto syncProperty = [=] { - const Qt::Alignment alignment(horizontalComboBox->currentData().toInt() | - verticalComboBox->currentData().toInt()); - property->setValue(QVariant::fromValue(alignment)); - }; - - syncEditor(); - - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(horizontalComboBox, qOverload(&QComboBox::currentIndexChanged), property, syncProperty); - QObject::connect(verticalComboBox, qOverload(&QComboBox::currentIndexChanged), property, syncProperty); + return editor; +} - return editor; - } -}; +QWidget *FontProperty::createEditor(QWidget *parent) +{ + auto editor = new QWidget(parent); + auto layout = new QVBoxLayout(editor); + auto fontComboBox = new QFontComboBox(editor); + auto sizeSpinBox = new QSpinBox(editor); + auto boldCheckBox = new QCheckBox(tr("Bold"), editor); + auto italicCheckBox = new QCheckBox(tr("Italic"), editor); + auto underlineCheckBox = new QCheckBox(tr("Underline"), editor); + auto strikeoutCheckBox = new QCheckBox(tr("Strikeout"), editor); + auto kerningCheckBox = new QCheckBox(tr("Kerning"), editor); + sizeSpinBox->setRange(1, 999); + sizeSpinBox->setSuffix(tr(" px")); + sizeSpinBox->setKeyboardTracking(false); + layout->setContentsMargins(QMargins()); + layout->setSpacing(Utils::dpiScaled(3)); + layout->addWidget(fontComboBox); + layout->addWidget(sizeSpinBox); + layout->addWidget(boldCheckBox); + layout->addWidget(italicCheckBox); + layout->addWidget(underlineCheckBox); + layout->addWidget(strikeoutCheckBox); + layout->addWidget(kerningCheckBox); + + auto syncEditor = [=] { + const auto font = this->value().value(); + const QSignalBlocker fontBlocker(fontComboBox); + const QSignalBlocker sizeBlocker(sizeSpinBox); + const QSignalBlocker boldBlocker(boldCheckBox); + const QSignalBlocker italicBlocker(italicCheckBox); + const QSignalBlocker underlineBlocker(underlineCheckBox); + const QSignalBlocker strikeoutBlocker(strikeoutCheckBox); + const QSignalBlocker kerningBlocker(kerningCheckBox); + fontComboBox->setCurrentFont(font); + sizeSpinBox->setValue(font.pixelSize()); + boldCheckBox->setChecked(font.bold()); + italicCheckBox->setChecked(font.italic()); + underlineCheckBox->setChecked(font.underline()); + strikeoutCheckBox->setChecked(font.strikeOut()); + kerningCheckBox->setChecked(font.kerning()); + }; + + auto syncProperty = [=] { + auto font = fontComboBox->currentFont(); + font.setPixelSize(sizeSpinBox->value()); + font.setBold(boldCheckBox->isChecked()); + font.setItalic(italicCheckBox->isChecked()); + font.setUnderline(underlineCheckBox->isChecked()); + font.setStrikeOut(strikeoutCheckBox->isChecked()); + font.setKerning(kerningCheckBox->isChecked()); + this->setValue(font); + }; + + syncEditor(); + + QObject::connect(this, &Property::valueChanged, fontComboBox, syncEditor); + QObject::connect(fontComboBox, &QFontComboBox::currentFontChanged, this, syncProperty); + QObject::connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), this, syncProperty); + QObject::connect(boldCheckBox, &QCheckBox::toggled, this, syncProperty); + QObject::connect(italicCheckBox, &QCheckBox::toggled, this, syncProperty); + QObject::connect(underlineCheckBox, &QCheckBox::toggled, this, syncProperty); + QObject::connect(strikeoutCheckBox, &QCheckBox::toggled, this, syncProperty); + QObject::connect(kerningCheckBox, &QCheckBox::toggled, this, syncProperty); + + return editor; +} + +QWidget *AlignmentProperty::createEditor(QWidget *parent) +{ + auto editor = new QWidget(parent); + auto layout = new QGridLayout(editor); + layout->setContentsMargins(QMargins()); + layout->setSpacing(Utils::dpiScaled(3)); + + auto horizontalLabel = new ElidingLabel(tr("Horizontal"), editor); + layout->addWidget(horizontalLabel, 0, 0); + + auto verticalLabel = new ElidingLabel(tr("Vertical"), editor); + layout->addWidget(verticalLabel, 1, 0); + + auto horizontalComboBox = new QComboBox(editor); + horizontalComboBox->addItem(tr("Left"), Qt::AlignLeft); + horizontalComboBox->addItem(tr("Center"), Qt::AlignHCenter); + horizontalComboBox->addItem(tr("Right"), Qt::AlignRight); + horizontalComboBox->addItem(tr("Justify"), Qt::AlignJustify); + layout->addWidget(horizontalComboBox, 0, 1); + + auto verticalComboBox = new QComboBox(editor); + verticalComboBox->addItem(tr("Top"), Qt::AlignTop); + verticalComboBox->addItem(tr("Center"), Qt::AlignVCenter); + verticalComboBox->addItem(tr("Bottom"), Qt::AlignBottom); + layout->addWidget(verticalComboBox, 1, 1); + + layout->setColumnStretch(1, 1); + + auto syncEditor = [=] { + const QSignalBlocker horizontalBlocker(horizontalComboBox); + const QSignalBlocker verticalBlocker(verticalComboBox); + const auto alignment = this->value().value(); + horizontalComboBox->setCurrentIndex(horizontalComboBox->findData(static_cast(alignment & Qt::AlignHorizontal_Mask))); + verticalComboBox->setCurrentIndex(verticalComboBox->findData(static_cast(alignment & Qt::AlignVertical_Mask))); + }; + + auto syncProperty = [=] { + const Qt::Alignment alignment(horizontalComboBox->currentData().toInt() | + verticalComboBox->currentData().toInt()); + this->setValue(QVariant::fromValue(alignment)); + }; + + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(horizontalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); + QObject::connect(verticalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); + + return editor; +} ValueProperty::ValueProperty(const QString &name, @@ -676,21 +617,6 @@ QWidget *EnumEditorFactory::createEditor(Property *property, QWidget *parent) ValueTypeEditorFactory::ValueTypeEditorFactory() { - // Register some useful default editor factories - registerEditorFactory(QMetaType::Bool, std::make_unique()); - registerEditorFactory(QMetaType::Double, std::make_unique()); - registerEditorFactory(QMetaType::Int, std::make_unique()); - registerEditorFactory(QMetaType::QColor, std::make_unique()); - registerEditorFactory(QMetaType::QFont, std::make_unique()); - registerEditorFactory(QMetaType::QPoint, std::make_unique()); - registerEditorFactory(QMetaType::QPointF, std::make_unique()); - registerEditorFactory(QMetaType::QRect, std::make_unique()); - registerEditorFactory(QMetaType::QRectF, std::make_unique()); - registerEditorFactory(QMetaType::QSize, std::make_unique()); - registerEditorFactory(QMetaType::QSizeF, std::make_unique()); - registerEditorFactory(QMetaType::QString, std::make_unique()); - registerEditorFactory(QMetaType::QUrl, std::make_unique()); - registerEditorFactory(qMetaTypeId(), std::make_unique()); } void ValueTypeEditorFactory::registerEditorFactory(int type, std::unique_ptr factory) @@ -723,10 +649,54 @@ ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, : nullptr); } -AbstractProperty *ValueTypeEditorFactory::createProperty(const QString &name, - std::function get, - std::function set) +template +Property *createTypedProperty(const QString &name, + std::function get, + std::function set) +{ + return new PropertyClass(name, + [get = std::move(get)] { return get().value(); }, + [set = std::move(set)] (typename PropertyClass::ValueType v) { set(QVariant::fromValue(v)); }); +} + +Property *ValueTypeEditorFactory::createProperty(const QString &name, + std::function get, + std::function set) { + const auto type = get().userType(); + switch (type) { + case QMetaType::QString: + return createTypedProperty(name, get, set); + case QMetaType::QUrl: + return createTypedProperty(name, get, set); + case QMetaType::Int: + return createTypedProperty(name, get, set); + case QMetaType::Double: + return createTypedProperty(name, get, set); + case QMetaType::Bool: + return createTypedProperty(name, get, set); + case QMetaType::QColor: + return createTypedProperty(name, get, set); + case QMetaType::QFont: + return createTypedProperty(name, get, set); + case QMetaType::QPoint: + return createTypedProperty(name, get, set); + case QMetaType::QPointF: + return createTypedProperty(name, get, set); + case QMetaType::QRect: + return createTypedProperty(name, get, set); + case QMetaType::QRectF: + return createTypedProperty(name, get, set); + case QMetaType::QSize: + return createTypedProperty(name, get, set); + case QMetaType::QSizeF: + return createTypedProperty(name, get, set); + default: + if (type == qMetaTypeId()) + return createTypedProperty(name, get, set); + } + + // Fall back to registered factories approach (still used for enums) auto f = m_factories.find(get().userType()); return new GetSetProperty(name, get, set, f != m_factories.end() ? f->second.get() diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 1e05876637..63df00b6d9 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -101,21 +101,131 @@ class EditorFactory virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; }; -struct IntEditorFactory : EditorFactory +/** + * A helper class for creating a property that wraps a value of a given type. + */ +template +class PropertyTemplate : public Property { - QWidget *createEditor(Property *property, QWidget *parent) override; +public: + using ValueType = Type; + + PropertyTemplate(const QString &name, + std::function get, + std::function set = {}, + QObject *parent = nullptr) + : Property(name, parent) + , m_get(std::move(get)) + , m_set(std::move(set)) + {} + + QVariant value() const override + { + return QVariant::fromValue(m_get()); + } + + void setValue(const QVariant &value) override + { + if (m_set) + m_set(value.value()); + } + +protected: + std::function m_get; + std::function m_set; }; -struct FloatEditorFactory : EditorFactory +struct StringProperty : PropertyTemplate { - QWidget *createEditor(Property *property, QWidget *parent) override; + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; - void setSuffix(const QString &suffix) { m_suffix = suffix; } +struct UrlProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; + void setFilter(const QString &filter) { m_filter = filter; } +private: + QString m_filter; +}; +struct IntProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct FloatProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; + void setSuffix(const QString &suffix) { m_suffix = suffix; } private: QString m_suffix; }; +struct BoolProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct PointProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct PointFProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct SizeProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct SizeFProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct RectProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct RectFProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +// todo: needs to handle invalid color (unset value) +struct ColorProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct FontProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + +struct AlignmentProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + /** * An editor factory that creates a combo box for enum properties. */ @@ -269,9 +379,9 @@ class ValueTypeEditorFactory : public EditorFactory * property will use the editor factory registered for the type of the * value. */ - AbstractProperty *createProperty(const QString &name, - std::function get, - std::function set); + Property *createProperty(const QString &name, + std::function get, + std::function set); QWidget *createEditor(Property *property, QWidget *parent) override; From ddbf1d6f5cb090a18dec3d81c78050b7d4680bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 6 Sep 2024 19:26:24 +0200 Subject: [PATCH 15/78] Renamed ValueTypeEditorFactory to PropertyFactory This name is more appropriate for what it actually does. It also no longer derives from EditorFactory. PropertyFactory::createQObjectProperty should be functional again. It now returns an appropriate property, and the QObjectProperty class is gone. This approach is still not used, however. --- src/tiled/propertieswidget.cpp | 152 ++++++++++++++++----------------- src/tiled/propertieswidget.h | 4 +- src/tiled/varianteditor.cpp | 94 +++++++------------- src/tiled/varianteditor.h | 71 +++++---------- 4 files changed, 132 insertions(+), 189 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 525d1fecbd..a496ba897b 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -63,7 +63,7 @@ namespace Tiled { PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mPropertyBrowser(new VariantEditor(this)) - , mDefaultEditorFactory(std::make_unique()) + , mPropertyFactory(std::make_unique()) { mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); @@ -307,11 +307,11 @@ class MapProperties : public ObjectProperties public: MapProperties(MapDocument *document, - ValueTypeEditorFactory *editorFactory, + PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, document->map(), parent) { - mOrientationProperty = editorFactory->createProperty( + mOrientationProperty = propertyFactory->createProperty( tr("Orientation"), [this]() { return QVariant::fromValue(map()->orientation()); @@ -367,7 +367,7 @@ class MapProperties : public ObjectProperties value.toInt())); }); - mStaggerAxisProperty = editorFactory->createProperty( + mStaggerAxisProperty = propertyFactory->createProperty( tr("Stagger Axis"), [this]() { return QVariant::fromValue(map()->staggerAxis()); @@ -377,7 +377,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), staggerAxis)); }); - mStaggerIndexProperty = editorFactory->createProperty( + mStaggerIndexProperty = propertyFactory->createProperty( tr("Stagger Index"), [this]() { return QVariant::fromValue(map()->staggerIndex()); @@ -387,7 +387,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), staggerIndex)); }); - mParallaxOriginProperty = editorFactory->createProperty( + mParallaxOriginProperty = propertyFactory->createProperty( tr("Parallax Origin"), [this]() { return map()->parallaxOrigin(); @@ -396,7 +396,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), value.value())); }); - mLayerDataFormatProperty = editorFactory->createProperty( + mLayerDataFormatProperty = propertyFactory->createProperty( tr("Layer Data Format"), [this]() { return QVariant::fromValue(map()->layerDataFormat()); @@ -406,7 +406,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), layerDataFormat)); }); - mChunkSizeProperty = editorFactory->createProperty( + mChunkSizeProperty = propertyFactory->createProperty( tr("Output Chunk Size"), [this]() { return map()->chunkSize(); @@ -415,7 +415,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), value.toSize())); }); - mRenderOrderProperty = editorFactory->createProperty( + mRenderOrderProperty = propertyFactory->createProperty( tr("Tile Render Order"), [this]() { return QVariant::fromValue(map()->renderOrder()); @@ -425,7 +425,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), renderOrder)); }); - mCompressionLevelProperty = editorFactory->createProperty( + mCompressionLevelProperty = propertyFactory->createProperty( tr("Compression Level"), [this]() { return map()->compressionLevel(); @@ -434,7 +434,7 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), value.toInt())); }); - mBackgroundColorProperty = editorFactory->createProperty( + mBackgroundColorProperty = propertyFactory->createProperty( tr("Background Color"), [this]() { return map()->backgroundColor(); @@ -576,12 +576,12 @@ class LayerProperties : public ObjectProperties Q_OBJECT public: - LayerProperties(MapDocument *document, Layer *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + LayerProperties(MapDocument *document, Layer *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { // todo: would be nicer to avoid the SpinBox and use a custom widget // might also be nice to embed this in the header instead of using a property - mIdProperty = editorFactory->createProperty( + mIdProperty = propertyFactory->createProperty( tr("ID"), [this]() { return layer()->id(); }, [](const QVariant &) {}); @@ -589,21 +589,21 @@ class LayerProperties : public ObjectProperties // todo: the below should be able to apply to all selected layers - mNameProperty = editorFactory->createProperty( + mNameProperty = propertyFactory->createProperty( tr("Name"), [this]() { return layer()->name(); }, [this](const QVariant &value) { push(new SetLayerName(mapDocument(), { layer() }, value.toString())); }); - mVisibleProperty = editorFactory->createProperty( + mVisibleProperty = propertyFactory->createProperty( tr("Visible"), [this]() { return layer()->isVisible(); }, [this](const QVariant &value) { push(new SetLayerVisible(mapDocument(), { layer() }, value.toBool())); }); - mLockedProperty = editorFactory->createProperty( + mLockedProperty = propertyFactory->createProperty( tr("Locked"), [this]() { return layer()->isLocked(); }, [this](const QVariant &value) { @@ -612,21 +612,21 @@ class LayerProperties : public ObjectProperties // todo: value should be between 0 and 1, and would be nice to use a slider (replacing the one in Layers view) // todo: singleStep should be 0.1 - mOpacityProperty = editorFactory->createProperty( + mOpacityProperty = propertyFactory->createProperty( tr("Opacity"), [this]() { return layer()->opacity(); }, [this](const QVariant &value) { push(new SetLayerOpacity(mapDocument(), { layer() }, value.toReal())); }); - mTintColorProperty = editorFactory->createProperty( + mTintColorProperty = propertyFactory->createProperty( tr("Tint Color"), [this]() { return layer()->tintColor(); }, [this](const QVariant &value) { push(new SetLayerTintColor(mapDocument(), { layer() }, value.value())); }); - mOffsetProperty = editorFactory->createProperty( + mOffsetProperty = propertyFactory->createProperty( tr("Offset"), [this]() { return layer()->offset(); }, [this](const QVariant &value) { @@ -634,7 +634,7 @@ class LayerProperties : public ObjectProperties }); // todo: singleStep should be 0.1 - mParallaxFactorProperty = editorFactory->createProperty( + mParallaxFactorProperty = propertyFactory->createProperty( tr("Parallax Factor"), [this]() { return layer()->parallaxFactor(); }, [this](const QVariant &value) { @@ -709,18 +709,18 @@ class ImageLayerProperties : public LayerProperties Q_OBJECT public: - ImageLayerProperties(MapDocument *document, ImageLayer *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : LayerProperties(document, object, editorFactory, parent) + ImageLayerProperties(MapDocument *document, ImageLayer *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) + : LayerProperties(document, object, propertyFactory, parent) { // todo: set a file filter for selecting images (or map files?) - mImageProperty = editorFactory->createProperty( + mImageProperty = propertyFactory->createProperty( tr("Image Source"), [this]() { return imageLayer()->imageSource(); }, [this](const QVariant &value) { push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value.toUrl())); }); - mTransparentColorProperty = editorFactory->createProperty( + mTransparentColorProperty = propertyFactory->createProperty( tr("Transparent Color"), [this]() { return imageLayer()->transparentColor(); }, [this](const QVariant &value) { @@ -728,14 +728,14 @@ class ImageLayerProperties : public LayerProperties }); // todo: consider merging Repeat X and Y into a single property - mRepeatXProperty = editorFactory->createProperty( + mRepeatXProperty = propertyFactory->createProperty( tr("Repeat X"), [this]() { return imageLayer()->repeatX(); }, [this](const QVariant &value) { push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, value.toBool())); }); - mRepeatYProperty = editorFactory->createProperty( + mRepeatYProperty = propertyFactory->createProperty( tr("Repeat Y"), [this]() { return imageLayer()->repeatY(); }, [this](const QVariant &value) { @@ -792,17 +792,17 @@ class ObjectGroupProperties : public LayerProperties Q_OBJECT public: - ObjectGroupProperties(MapDocument *document, ObjectGroup *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) - : LayerProperties(document, object, editorFactory, parent) + ObjectGroupProperties(MapDocument *document, ObjectGroup *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) + : LayerProperties(document, object, propertyFactory, parent) { - mColorProperty = editorFactory->createProperty( + mColorProperty = propertyFactory->createProperty( tr("Color"), [this]() { return objectGroup()->color(); }, [this](const QVariant &value) { push(new ChangeObjectGroupColor(mapDocument(), { objectGroup() }, value.value())); }); - mDrawOrderProperty = editorFactory->createProperty( + mDrawOrderProperty = propertyFactory->createProperty( tr("Draw Order"), [this]() { return QVariant::fromValue(objectGroup()->drawOrder()); }, [this](const QVariant &value) { @@ -852,11 +852,11 @@ class TilesetProperties : public ObjectProperties public: TilesetProperties(TilesetDocument *document, - ValueTypeEditorFactory *editorFactory, + PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, document->tileset().data(), parent) { - mNameProperty = editorFactory->createProperty( + mNameProperty = propertyFactory->createProperty( tr("Name"), [this]() { return tilesetDocument()->tileset()->name(); @@ -865,7 +865,7 @@ class TilesetProperties : public ObjectProperties push(new RenameTileset(tilesetDocument(), value.toString())); }); - mObjectAlignmentProperty = editorFactory->createProperty( + mObjectAlignmentProperty = propertyFactory->createProperty( tr("Object Alignment"), [this]() { return QVariant::fromValue(tileset()->objectAlignment()); @@ -875,7 +875,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetObjectAlignment(tilesetDocument(), objectAlignment)); }); - mTileOffsetProperty = editorFactory->createProperty( + mTileOffsetProperty = propertyFactory->createProperty( tr("Drawing Offset"), [this]() { return tileset()->tileOffset(); @@ -884,7 +884,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetTileOffset(tilesetDocument(), value.value())); }); - mTileRenderSizeProperty = editorFactory->createProperty( + mTileRenderSizeProperty = propertyFactory->createProperty( tr("Tile Render Size"), [this]() { return QVariant::fromValue(tileset()->tileRenderSize()); @@ -894,7 +894,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetTileRenderSize(tilesetDocument(), tileRenderSize)); }); - mFillModeProperty = editorFactory->createProperty( + mFillModeProperty = propertyFactory->createProperty( tr("Fill Mode"), [this]() { return QVariant::fromValue(tileset()->fillMode()); @@ -904,7 +904,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetFillMode(tilesetDocument(), fillMode)); }); - mBackgroundColorProperty = editorFactory->createProperty( + mBackgroundColorProperty = propertyFactory->createProperty( tr("Background Color"), [this]() { return tileset()->backgroundColor(); @@ -913,7 +913,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetBackgroundColor(tilesetDocument(), value.value())); }); - mOrientationProperty = editorFactory->createProperty( + mOrientationProperty = propertyFactory->createProperty( tr("Orientation"), [this]() { return QVariant::fromValue(tileset()->orientation()); @@ -923,7 +923,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetOrientation(tilesetDocument(), orientation)); }); - mGridSizeProperty = editorFactory->createProperty( + mGridSizeProperty = propertyFactory->createProperty( tr("Grid Size"), [this]() { return tileset()->gridSize(); @@ -933,7 +933,7 @@ class TilesetProperties : public ObjectProperties }); // todo: needs 1 as minimum value - mColumnCountProperty = editorFactory->createProperty( + mColumnCountProperty = propertyFactory->createProperty( tr("Columns"), [this]() { return tileset()->columnCount(); @@ -943,7 +943,7 @@ class TilesetProperties : public ObjectProperties }); // todo: this needs a custom widget - mAllowedTransformationsProperty = editorFactory->createProperty( + mAllowedTransformationsProperty = propertyFactory->createProperty( tr("Allowed Transformations"), [this]() { return QVariant::fromValue(tileset()->transformationFlags()); @@ -954,7 +954,7 @@ class TilesetProperties : public ObjectProperties }); // todo: this needs a custom widget - mImageProperty = editorFactory->createProperty( + mImageProperty = propertyFactory->createProperty( tr("Image"), [this]() { return tileset()->imageSource().toString(); @@ -1058,7 +1058,7 @@ class MapObjectProperties : public ObjectProperties Q_OBJECT public: - MapObjectProperties(MapDocument *document, MapObject *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + MapObjectProperties(MapDocument *document, MapObject *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { mIdProperty = new IntProperty( @@ -1075,7 +1075,7 @@ class MapObjectProperties : public ObjectProperties }); mTemplateProperty->setEnabled(false); - mNameProperty = editorFactory->createProperty( + mNameProperty = propertyFactory->createProperty( tr("Name"), [this]() { return mapObject()->name(); @@ -1084,7 +1084,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::NameProperty, value); }); - mVisibleProperty = editorFactory->createProperty( + mVisibleProperty = propertyFactory->createProperty( tr("Visible"), [this]() { return mapObject()->isVisible(); @@ -1093,7 +1093,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::VisibleProperty, value); }); - mPositionProperty = editorFactory->createProperty( + mPositionProperty = propertyFactory->createProperty( tr("Position"), [this]() { return mapObject()->position(); @@ -1102,7 +1102,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::PositionProperty, value); }); - mSizeProperty = editorFactory->createProperty( + mSizeProperty = propertyFactory->createProperty( tr("Size"), [this]() { return mapObject()->size(); @@ -1123,7 +1123,7 @@ class MapObjectProperties : public ObjectProperties mRotationProperty->setSuffix(QStringLiteral("°")); // todo: make this a custom widget with "Horizontal" and "Vertical" checkboxes - mFlippingProperty = editorFactory->createProperty( + mFlippingProperty = propertyFactory->createProperty( tr("Flipping"), [this]() { return mapObject()->cell().flags(); @@ -1146,7 +1146,7 @@ class MapObjectProperties : public ObjectProperties push(command); }); - mTextProperty = editorFactory->createProperty( + mTextProperty = propertyFactory->createProperty( tr("Text"), [this]() { return mapObject()->textData().text; @@ -1155,7 +1155,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::TextProperty, value); }); - mTextAlignmentProperty = editorFactory->createProperty( + mTextAlignmentProperty = propertyFactory->createProperty( tr("Alignment"), [this]() { return QVariant::fromValue(mapObject()->textData().alignment); @@ -1164,7 +1164,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::TextAlignmentProperty, value); }); - mTextFontProperty = editorFactory->createProperty( + mTextFontProperty = propertyFactory->createProperty( tr("Font"), [this]() { return mapObject()->textData().font; @@ -1173,7 +1173,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::TextFontProperty, value); }); - mTextWordWrapProperty = editorFactory->createProperty( + mTextWordWrapProperty = propertyFactory->createProperty( tr("Word Wrap"), [this]() { return mapObject()->textData().wordWrap; @@ -1182,7 +1182,7 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::TextWordWrapProperty, value); }); - mTextColorProperty = editorFactory->createProperty( + mTextColorProperty = propertyFactory->createProperty( tr("Text Color"), [this]() { return mapObject()->textData().color; @@ -1320,17 +1320,17 @@ class TileProperties : public ObjectProperties Q_OBJECT public: - TileProperties(Document *document, Tile *object, ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + TileProperties(Document *document, Tile *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mIdProperty = editorFactory->createProperty( + mIdProperty = propertyFactory->createProperty( tr("ID"), [this]() { return tile()->id(); }, [](const QVariant &) {}); mIdProperty->setEnabled(false); // todo: apply readableImageFormatsFilter - mImageProperty = editorFactory->createProperty( + mImageProperty = propertyFactory->createProperty( tr("Image"), [this]() { return tile()->imageSource(); }, [this](const QVariant &value) { @@ -1339,7 +1339,7 @@ class TileProperties : public ObjectProperties value.toUrl())); }); - mRectangleProperty = editorFactory->createProperty( + mRectangleProperty = propertyFactory->createProperty( tr("Rectangle"), [this]() { return tile()->imageRect(); }, [this](const QVariant &value) { @@ -1349,7 +1349,7 @@ class TileProperties : public ObjectProperties }); // todo: minimum value should be 0 - mProbabilityProperty = editorFactory->createProperty( + mProbabilityProperty = propertyFactory->createProperty( tr("Probability"), [this]() { return tile()->probability(); }, [this](const QVariant &value) { @@ -1439,17 +1439,17 @@ class WangSetProperties : public ObjectProperties public: WangSetProperties(Document *document, WangSet *object, - ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mNameProperty = editorFactory->createProperty( + mNameProperty = propertyFactory->createProperty( tr("Name"), [this]() { return wangSet()->name(); }, [this](const QVariant &value) { push(new RenameWangSet(tilesetDocument(), wangSet(), value.toString())); }); - mTypeProperty = editorFactory->createProperty( + mTypeProperty = propertyFactory->createProperty( tr("Type"), [this]() { return QVariant::fromValue(wangSet()->type()); }, [this](const QVariant &value) { @@ -1457,7 +1457,7 @@ class WangSetProperties : public ObjectProperties }); // todo: keep between 0 and WangId::MAX_COLOR_COUNT - mColorCountProperty = editorFactory->createProperty( + mColorCountProperty = propertyFactory->createProperty( tr("Color Count"), [this]() { return wangSet()->colorCount(); }, [this](const QVariant &value) { @@ -1536,17 +1536,17 @@ class WangColorProperties : public ObjectProperties public: WangColorProperties(Document *document, WangColor *object, - ValueTypeEditorFactory *editorFactory, QObject *parent = nullptr) + PropertyFactory *propertyFactory, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mNameProperty = editorFactory->createProperty( + mNameProperty = propertyFactory->createProperty( tr("Name"), [this]() { return wangColor()->name(); }, [this](const QVariant &value) { push(new ChangeWangColorName(tilesetDocument(), wangColor(), value.toString())); }); - mColorProperty = editorFactory->createProperty( + mColorProperty = propertyFactory->createProperty( tr("Color"), [this]() { return wangColor()->color(); }, [this](const QVariant &value) { @@ -1554,7 +1554,7 @@ class WangColorProperties : public ObjectProperties }); // todo: set 0.01 as minimum - mProbabilityProperty = editorFactory->createProperty( + mProbabilityProperty = propertyFactory->createProperty( tr("Probability"), [this]() { return wangColor()->probability(); }, [this](const QVariant &value) { @@ -1643,48 +1643,48 @@ void PropertiesWidget::currentObjectChanged(Object *object) case Layer::ImageLayerType: mPropertiesObject = new ImageLayerProperties(mapDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Layer::ObjectGroupType: mPropertiesObject = new ObjectGroupProperties(mapDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Layer::TileLayerType: case Layer::GroupLayerType: mPropertiesObject = new LayerProperties(mapDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; } break; } case Object::MapObjectType: mPropertiesObject = new MapObjectProperties(static_cast(mDocument), - static_cast(object), mDefaultEditorFactory.get(), this); + static_cast(object), mPropertyFactory.get(), this); break; case Object::MapType: mPropertiesObject = new MapProperties(static_cast(mDocument), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Object::TilesetType: mPropertiesObject = new TilesetProperties(static_cast(mDocument), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Object::TileType: mPropertiesObject = new TileProperties(mDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Object::WangSetType: mPropertiesObject = new WangSetProperties(mDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Object::WangColorType: mPropertiesObject = new WangColorProperties(mDocument, static_cast(object), - mDefaultEditorFactory.get(), this); + mPropertyFactory.get(), this); break; case Object::ProjectType: case Object::WorldType: @@ -2203,7 +2203,7 @@ void PropertiesWidget::registerEditorFactories() void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) { - mDefaultEditorFactory->registerEditorFactory(type, std::move(factory)); + mPropertyFactory->registerEditorFactory(type, std::move(factory)); } void PropertiesWidget::retranslateUi() diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index d6ad390af1..c3a2bb0088 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -31,7 +31,7 @@ class Object; class Document; class EditorFactory; class ObjectProperties; -class ValueTypeEditorFactory; +class PropertyFactory; class VariantEditor; /** @@ -84,7 +84,7 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; VariantEditor *mPropertyBrowser; - std::unique_ptr mDefaultEditorFactory; + std::unique_ptr mPropertyFactory; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 52ae9513a4..f73b582fb0 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -615,33 +615,48 @@ QWidget *EnumEditorFactory::createEditor(Property *property, QWidget *parent) } -ValueTypeEditorFactory::ValueTypeEditorFactory() -{ -} - -void ValueTypeEditorFactory::registerEditorFactory(int type, std::unique_ptr factory) +void PropertyFactory::registerEditorFactory(int type, std::unique_ptr factory) { m_factories[type] = std::move(factory); } -QObjectProperty *ValueTypeEditorFactory::createQObjectProperty(QObject *qObject, - const char *name, - const QString &displayName) +Property *PropertyFactory::createQObjectProperty(QObject *qObject, + const char *name, + const QString &displayName) { auto metaObject = qObject->metaObject(); auto propertyIndex = metaObject->indexOfProperty(name); if (propertyIndex < 0) return nullptr; - return new QObjectProperty(qObject, - metaObject->property(propertyIndex), - displayName.isEmpty() ? QString::fromUtf8(name) - : displayName, - this); + auto metaProperty = metaObject->property(propertyIndex); + auto property = createProperty(displayName.isEmpty() ? QString::fromUtf8(name) + : displayName, + [=] { + return metaProperty.read(qObject); + }, + [=] (const QVariant &value) { + metaProperty.write(qObject, value); + }); + + // If the property has a notify signal, forward it to valueChanged + auto notify = metaProperty.notifySignal(); + if (notify.isValid()) { + auto propertyMetaObject = property->metaObject(); + auto valuePropertyIndex = propertyMetaObject->indexOfProperty("value"); + auto valueProperty = propertyMetaObject->property(valuePropertyIndex); + auto valueChanged = valueProperty.notifySignal(); + + QObject::connect(qObject, notify, property, valueChanged); + } + + property->setEnabled(metaProperty.isWritable()); + + return property; } -ValueProperty *ValueTypeEditorFactory::createProperty(const QString &name, - const QVariant &value) +ValueProperty *PropertyFactory::createProperty(const QString &name, + const QVariant &value) { auto f = m_factories.find(value.userType()); return new ValueProperty(name, value, @@ -659,9 +674,9 @@ Property *createTypedProperty(const QString &name, [set = std::move(set)] (typename PropertyClass::ValueType v) { set(QVariant::fromValue(v)); }); } -Property *ValueTypeEditorFactory::createProperty(const QString &name, - std::function get, - std::function set) +Property *PropertyFactory::createProperty(const QString &name, + std::function get, + std::function set) { const auto type = get().userType(); switch (type) { @@ -703,49 +718,6 @@ Property *ValueTypeEditorFactory::createProperty(const QString &name, : nullptr); } -QWidget *ValueTypeEditorFactory::createEditor(Property *property, QWidget *parent) -{ - const auto value = property->value(); - const int type = value.userType(); - auto factory = m_factories.find(type); - if (factory != m_factories.end()) - return factory->second->createEditor(property, parent); - return nullptr; -} - - -QObjectProperty::QObjectProperty(QObject *object, - QMetaProperty property, - const QString &displayName, - EditorFactory *editorFactory, - QObject *parent) - : AbstractProperty(displayName, editorFactory, parent) - , m_object(object) - , m_property(property) -{ - // If the property has a notify signal, forward it to valueChanged - auto notify = property.notifySignal(); - if (notify.isValid()) { - auto valuePropertyIndex = metaObject()->indexOfProperty("value"); - auto valueProperty = metaObject()->property(valuePropertyIndex); - auto valueChanged = valueProperty.notifySignal(); - - connect(m_object, notify, this, valueChanged); - } - - setEnabled(m_property.isWritable()); -} - -QVariant QObjectProperty::value() const -{ - return m_property.read(m_object); -} - -void QObjectProperty::setValue(const QVariant &value) -{ - m_property.write(m_object, value); -} - } // namespace Tiled #include "moc_varianteditor.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 63df00b6d9..7c257b9bc8 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -88,19 +88,6 @@ class Property : public QObject bool m_enabled = true; }; -/** - * An editor factory is responsible for creating an editor widget for a given - * property. It can be used to share the configuration of editor widgets - * between different properties. - */ -class EditorFactory -{ - Q_DECLARE_TR_FUNCTIONS(EditorFactory) - -public: - virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; -}; - /** * A helper class for creating a property that wraps a value of a given type. */ @@ -226,6 +213,19 @@ struct AlignmentProperty : PropertyTemplate QWidget *createEditor(QWidget *parent) override; }; +/** + * An editor factory is responsible for creating an editor widget for a given + * property. It can be used to share the configuration of editor widgets + * between different properties. + */ +class EditorFactory +{ + Q_DECLARE_TR_FUNCTIONS(EditorFactory) + +public: + virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; +}; + /** * An editor factory that creates a combo box for enum properties. */ @@ -316,41 +316,15 @@ class ValueProperty : public AbstractProperty QVariant m_value; }; -/** - * A property that wraps a value of a QObject property and uses an editor - * factory to create its editor. - * - * The property does not take ownership of the editor factory. - */ -class QObjectProperty final : public AbstractProperty -{ - Q_OBJECT - -public: - QObjectProperty(QObject *object, - QMetaProperty property, - const QString &displayName, - EditorFactory *editorFactory, - QObject *parent = nullptr); - - QVariant value() const override; - void setValue(const QVariant &value) override; - -private: - QObject *m_object; - QMetaProperty m_property; -}; /** - * An editor factory that selects the appropriate editor factory based on the - * type of the property value. - * - * todo: rename to VariantEditorFactory when the old one is removed + * A property factory that instantiates the appropriate property type based on + * the type of the property value. */ -class ValueTypeEditorFactory : public EditorFactory +class PropertyFactory { public: - ValueTypeEditorFactory(); + PropertyFactory() = default; /** * Register an editor factory for a given type. @@ -361,12 +335,11 @@ class ValueTypeEditorFactory : public EditorFactory void registerEditorFactory(int type, std::unique_ptr factory); /** - * Creates a property that wraps a QObject property and will use the editor - * factory registered for the type of the value. + * Creates a property that wraps a QObject property. */ - QObjectProperty *createQObjectProperty(QObject *qObject, - const char *name, - const QString &displayName = {}); + Property *createQObjectProperty(QObject *qObject, + const char *name, + const QString &displayName = {}); /** * Creates a property with the given name and value. The property will use @@ -383,8 +356,6 @@ class ValueTypeEditorFactory : public EditorFactory std::function get, std::function set); - QWidget *createEditor(Property *property, QWidget *parent) override; - private: std::unordered_map> m_factories; }; From 3211a461fd91ef60049bc9768d44353cc71c8e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 9 Sep 2024 12:59:42 +0200 Subject: [PATCH 16/78] Simplifying or over-complicating things * Removed EditorFactory, EnumEditorFactory, AbstractProperty, ValueProperty and GetSetProperty. * EnumProperty now derives from IntProperty and retrieves the enum meta-data based on its template argument. * Use the typed properties to avoid QVariant when synchronizing between the created editor. For the built-in properties, this definitely simplifies things. But it remains to be seen how custom properties are best handled. Also due to the lack of registering editor factories, QObjectProperty is now broken for enums (but it's unused). --- src/tiled/propertieswidget.cpp | 767 ++++++++++++++++----------------- src/tiled/propertieswidget.h | 8 - src/tiled/varianteditor.cpp | 194 +++------ src/tiled/varianteditor.h | 203 +++------ 4 files changed, 487 insertions(+), 685 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index a496ba897b..e97b0dd15b 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -60,10 +60,142 @@ namespace Tiled { +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("Alignment", "Unspecified"), + QCoreApplication::translate("Alignment", "Top Left"), + QCoreApplication::translate("Alignment", "Top"), + QCoreApplication::translate("Alignment", "Top Right"), + QCoreApplication::translate("Alignment", "Left"), + QCoreApplication::translate("Alignment", "Center"), + QCoreApplication::translate("Alignment", "Right"), + QCoreApplication::translate("Alignment", "Bottom Left"), + QCoreApplication::translate("Alignment", "Bottom"), + QCoreApplication::translate("Alignment", "Bottom Right") + }}; +} + +template<> EnumData enumData() +{ + // We leave out the "Unknown" orientation, because it shouldn't occur here + return {{ + QCoreApplication::translate("Tiled::NewMapDialog", "Orthogonal"), + QCoreApplication::translate("Tiled::NewMapDialog", "Isometric"), + QCoreApplication::translate("Tiled::NewMapDialog", "Isometric (Staggered)"), + QCoreApplication::translate("Tiled::NewMapDialog", "Hexagonal (Staggered)") + }, { + Map::Orthogonal, + Map::Isometric, + Map::Staggered, + Map::Hexagonal, + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("StaggerAxis", "X"), + QCoreApplication::translate("StaggerAxis", "Y") + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("StaggerIndex", "Odd"), + QCoreApplication::translate("StaggerIndex", "Even") + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("RenderOrder", "Right Down"), + QCoreApplication::translate("RenderOrder", "Right Up"), + QCoreApplication::translate("RenderOrder", "Left Down"), + QCoreApplication::translate("RenderOrder", "Left Up") + }}; +} + +template<> EnumData enumData() +{ + QStringList names { + QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), + QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), + }; + QList values { + Map::XML, + Map::Base64, + Map::Base64Gzip, + Map::Base64Zlib, + }; + + if (compressionSupported(Zstandard)) { + names.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); + values.append(Map::Base64Zstandard); + } + + names.append(QCoreApplication::translate("PreferencesDialog", "CSV")); + values.append(Map::CSV); + + return { names, values }; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("Tileset", "Orthogonal"), + QCoreApplication::translate("Tileset", "Isometric"), + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("Tileset", "Tile Size"), + QCoreApplication::translate("Tileset", "Map Grid Size"), + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("Tileset", "Stretch"), + QCoreApplication::translate("Tileset", "Preserve Aspect Ratio"), + }}; +} + +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("ObjectGroup", "Top Down"), + QCoreApplication::translate("ObjectGroup", "Index Order"), + }}; +} + +template<> EnumData enumData() +{ + const QStringList names { + QCoreApplication::translate("WangSet", "Corner"), + QCoreApplication::translate("WangSet", "Edge"), + QCoreApplication::translate("WangSet", "Mixed"), + }; + + QMap icons; + icons.insert(WangSet::Corner, wangSetIcon(WangSet::Corner)); + icons.insert(WangSet::Edge, wangSetIcon(WangSet::Edge)); + icons.insert(WangSet::Mixed, wangSetIcon(WangSet::Mixed)); + + return { names, {}, icons }; +} + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mPropertyBrowser(new VariantEditor(this)) - , mPropertyFactory(std::make_unique()) { mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); @@ -109,7 +241,6 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, // this, &PropertiesWidget::updateActions); - registerEditorFactories(); retranslateUi(); } @@ -201,11 +332,11 @@ class ClassProperty : public StringProperty editor->addItems(classNamesFor(*mObject)); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setCurrentText(value().toString()); + editor->setCurrentText(value()); }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, &QComboBox::currentTextChanged, this, &Property::setValue); + connect(editor, &QComboBox::currentTextChanged, this, &StringProperty::setValue); connect(Preferences::instance(), &Preferences::propertyTypesChanged, editor, [this,editor] { editor->clear(); @@ -306,19 +437,16 @@ class MapProperties : public ObjectProperties Q_OBJECT public: - MapProperties(MapDocument *document, - PropertyFactory *propertyFactory, - QObject *parent = nullptr) + MapProperties(MapDocument *document, QObject *parent = nullptr) : ObjectProperties(document, document->map(), parent) { - mOrientationProperty = propertyFactory->createProperty( + mOrientationProperty = new EnumProperty( tr("Orientation"), - [this]() { - return QVariant::fromValue(map()->orientation()); + [this] { + return map()->orientation(); }, - [this](const QVariant &value) { - auto orientation = static_cast(value.toInt()); - push(new ChangeMapProperty(mapDocument(), orientation)); + [this](Map::Orientation value) { + push(new ChangeMapProperty(mapDocument(), value)); }); mSizeProperty = new MapSizeProperty(mapDocument(), this); @@ -347,7 +475,7 @@ class MapProperties : public ObjectProperties mInfiniteProperty = new BoolProperty( tr("Infinite"), - [this]() { + [this] { return map()->infinite(); }, [this](const bool &value) { @@ -358,7 +486,7 @@ class MapProperties : public ObjectProperties mHexSideLengthProperty = new IntProperty( tr("Hex Side Length"), - [this]() { + [this] { return map()->hexSideLength(); }, [this](const QVariant &value) { @@ -367,80 +495,76 @@ class MapProperties : public ObjectProperties value.toInt())); }); - mStaggerAxisProperty = propertyFactory->createProperty( + mStaggerAxisProperty = new EnumProperty( tr("Stagger Axis"), - [this]() { - return QVariant::fromValue(map()->staggerAxis()); + [this] { + return map()->staggerAxis(); }, - [this](const QVariant &value) { - auto staggerAxis = static_cast(value.toInt()); - push(new ChangeMapProperty(mapDocument(), staggerAxis)); + [this](Map::StaggerAxis value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mStaggerIndexProperty = propertyFactory->createProperty( + mStaggerIndexProperty = new EnumProperty( tr("Stagger Index"), - [this]() { - return QVariant::fromValue(map()->staggerIndex()); + [this] { + return map()->staggerIndex(); }, - [this](const QVariant &value) { - auto staggerIndex = static_cast(value.toInt()); - push(new ChangeMapProperty(mapDocument(), staggerIndex)); + [this](Map::StaggerIndex value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mParallaxOriginProperty = propertyFactory->createProperty( + mParallaxOriginProperty = new PointFProperty( tr("Parallax Origin"), - [this]() { + [this] { return map()->parallaxOrigin(); }, - [this](const QVariant &value) { - push(new ChangeMapProperty(mapDocument(), value.value())); + [this](const QPointF &value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mLayerDataFormatProperty = propertyFactory->createProperty( + mLayerDataFormatProperty = new EnumProperty( tr("Layer Data Format"), - [this]() { - return QVariant::fromValue(map()->layerDataFormat()); + [this] { + return map()->layerDataFormat(); }, - [this](const QVariant &value) { - auto layerDataFormat = static_cast(value.toInt()); - push(new ChangeMapProperty(mapDocument(), layerDataFormat)); + [this](Map::LayerDataFormat value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mChunkSizeProperty = propertyFactory->createProperty( + mChunkSizeProperty = new SizeProperty( tr("Output Chunk Size"), - [this]() { + [this] { return map()->chunkSize(); }, - [this](const QVariant &value) { - push(new ChangeMapProperty(mapDocument(), value.toSize())); + [this](const QSize &value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mRenderOrderProperty = propertyFactory->createProperty( + mRenderOrderProperty = new EnumProperty( tr("Tile Render Order"), - [this]() { - return QVariant::fromValue(map()->renderOrder()); + [this] { + return map()->renderOrder(); }, - [this](const QVariant &value) { - auto renderOrder = static_cast(value.toInt()); - push(new ChangeMapProperty(mapDocument(), renderOrder)); + [this](Map::RenderOrder value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mCompressionLevelProperty = propertyFactory->createProperty( + mCompressionLevelProperty = new IntProperty( tr("Compression Level"), - [this]() { + [this] { return map()->compressionLevel(); }, - [this](const QVariant &value) { - push(new ChangeMapProperty(mapDocument(), value.toInt())); + [this](const int &value) { + push(new ChangeMapProperty(mapDocument(), value)); }); - mBackgroundColorProperty = propertyFactory->createProperty( + mBackgroundColorProperty = new ColorProperty( tr("Background Color"), - [this]() { + [this] { return map()->backgroundColor(); }, - [this](const QVariant &value) { - push(new ChangeMapProperty(mapDocument(), value.value())); + [this](const QColor &value) { + push(new ChangeMapProperty(mapDocument(), value)); }); updateEnabledState(); @@ -576,69 +700,68 @@ class LayerProperties : public ObjectProperties Q_OBJECT public: - LayerProperties(MapDocument *document, Layer *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) + LayerProperties(MapDocument *document, Layer *object, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { // todo: would be nicer to avoid the SpinBox and use a custom widget // might also be nice to embed this in the header instead of using a property - mIdProperty = propertyFactory->createProperty( + mIdProperty = new IntProperty( tr("ID"), - [this]() { return layer()->id(); }, - [](const QVariant &) {}); + [this] { return layer()->id(); }); mIdProperty->setEnabled(false); // todo: the below should be able to apply to all selected layers - mNameProperty = propertyFactory->createProperty( + mNameProperty = new StringProperty( tr("Name"), - [this]() { return layer()->name(); }, - [this](const QVariant &value) { - push(new SetLayerName(mapDocument(), { layer() }, value.toString())); + [this] { return layer()->name(); }, + [this](const QString &value) { + push(new SetLayerName(mapDocument(), { layer() }, value)); }); - mVisibleProperty = propertyFactory->createProperty( + mVisibleProperty = new BoolProperty( tr("Visible"), - [this]() { return layer()->isVisible(); }, - [this](const QVariant &value) { - push(new SetLayerVisible(mapDocument(), { layer() }, value.toBool())); + [this] { return layer()->isVisible(); }, + [this](const bool &value) { + push(new SetLayerVisible(mapDocument(), { layer() }, value)); }); - mLockedProperty = propertyFactory->createProperty( + mLockedProperty = new BoolProperty( tr("Locked"), - [this]() { return layer()->isLocked(); }, - [this](const QVariant &value) { - push(new SetLayerLocked(mapDocument(), { layer() }, value.toBool())); + [this] { return layer()->isLocked(); }, + [this](const bool &value) { + push(new SetLayerLocked(mapDocument(), { layer() }, value)); }); // todo: value should be between 0 and 1, and would be nice to use a slider (replacing the one in Layers view) // todo: singleStep should be 0.1 - mOpacityProperty = propertyFactory->createProperty( + mOpacityProperty = new FloatProperty( tr("Opacity"), - [this]() { return layer()->opacity(); }, - [this](const QVariant &value) { - push(new SetLayerOpacity(mapDocument(), { layer() }, value.toReal())); + [this] { return layer()->opacity(); }, + [this](const double &value) { + push(new SetLayerOpacity(mapDocument(), { layer() }, value)); }); - mTintColorProperty = propertyFactory->createProperty( + mTintColorProperty = new ColorProperty( tr("Tint Color"), - [this]() { return layer()->tintColor(); }, - [this](const QVariant &value) { - push(new SetLayerTintColor(mapDocument(), { layer() }, value.value())); + [this] { return layer()->tintColor(); }, + [this](const QColor &value) { + push(new SetLayerTintColor(mapDocument(), { layer() }, value)); }); - mOffsetProperty = propertyFactory->createProperty( + mOffsetProperty = new PointFProperty( tr("Offset"), - [this]() { return layer()->offset(); }, - [this](const QVariant &value) { - push(new SetLayerOffset(mapDocument(), { layer() }, value.value())); + [this] { return layer()->offset(); }, + [this](const QPointF &value) { + push(new SetLayerOffset(mapDocument(), { layer() }, value)); }); // todo: singleStep should be 0.1 - mParallaxFactorProperty = propertyFactory->createProperty( + mParallaxFactorProperty = new PointFProperty( tr("Parallax Factor"), - [this]() { return layer()->parallaxFactor(); }, - [this](const QVariant &value) { - push(new SetLayerParallaxFactor(mapDocument(), { layer() }, value.toPointF())); + [this] { return layer()->parallaxFactor(); }, + [this](const QPointF &value) { + push(new SetLayerParallaxFactor(mapDocument(), { layer() }, value)); }); connect(document, &Document::changed, @@ -709,37 +832,37 @@ class ImageLayerProperties : public LayerProperties Q_OBJECT public: - ImageLayerProperties(MapDocument *document, ImageLayer *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) - : LayerProperties(document, object, propertyFactory, parent) + ImageLayerProperties(MapDocument *document, ImageLayer *object, QObject *parent = nullptr) + : LayerProperties(document, object, parent) { // todo: set a file filter for selecting images (or map files?) - mImageProperty = propertyFactory->createProperty( + mImageProperty = new UrlProperty( tr("Image Source"), - [this]() { return imageLayer()->imageSource(); }, - [this](const QVariant &value) { - push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value.toUrl())); + [this] { return imageLayer()->imageSource(); }, + [this](const QUrl &value) { + push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value)); }); - mTransparentColorProperty = propertyFactory->createProperty( + mTransparentColorProperty = new ColorProperty( tr("Transparent Color"), - [this]() { return imageLayer()->transparentColor(); }, - [this](const QVariant &value) { - push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value.value())); + [this] { return imageLayer()->transparentColor(); }, + [this](const QColor &value) { + push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value)); }); // todo: consider merging Repeat X and Y into a single property - mRepeatXProperty = propertyFactory->createProperty( + mRepeatXProperty = new BoolProperty( tr("Repeat X"), - [this]() { return imageLayer()->repeatX(); }, - [this](const QVariant &value) { - push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, value.toBool())); + [this] { return imageLayer()->repeatX(); }, + [this](const bool &value) { + push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, value)); }); - mRepeatYProperty = propertyFactory->createProperty( + mRepeatYProperty = new BoolProperty( tr("Repeat Y"), - [this]() { return imageLayer()->repeatY(); }, - [this](const QVariant &value) { - push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, value.toBool())); + [this] { return imageLayer()->repeatY(); }, + [this](const bool &value) { + push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, value)); }); } @@ -792,22 +915,21 @@ class ObjectGroupProperties : public LayerProperties Q_OBJECT public: - ObjectGroupProperties(MapDocument *document, ObjectGroup *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) - : LayerProperties(document, object, propertyFactory, parent) + ObjectGroupProperties(MapDocument *document, ObjectGroup *object, QObject *parent = nullptr) + : LayerProperties(document, object, parent) { - mColorProperty = propertyFactory->createProperty( + mColorProperty = new ColorProperty( tr("Color"), - [this]() { return objectGroup()->color(); }, - [this](const QVariant &value) { - push(new ChangeObjectGroupColor(mapDocument(), { objectGroup() }, value.value())); + [this] { return objectGroup()->color(); }, + [this](const QColor &value) { + push(new ChangeObjectGroupColor(mapDocument(), { objectGroup() }, value)); }); - mDrawOrderProperty = propertyFactory->createProperty( + mDrawOrderProperty = new EnumProperty( tr("Draw Order"), - [this]() { return QVariant::fromValue(objectGroup()->drawOrder()); }, - [this](const QVariant &value) { - ObjectGroup::DrawOrder drawOrder = static_cast(value.toInt()); - push(new ChangeObjectGroupDrawOrder(mapDocument(), { objectGroup() }, drawOrder)); + [this] { return objectGroup()->drawOrder(); }, + [this](ObjectGroup::DrawOrder value) { + push(new ChangeObjectGroupDrawOrder(mapDocument(), { objectGroup() }, value)); }); } @@ -851,115 +973,109 @@ class TilesetProperties : public ObjectProperties Q_OBJECT public: - TilesetProperties(TilesetDocument *document, - PropertyFactory *propertyFactory, - QObject *parent = nullptr) + TilesetProperties(TilesetDocument *document, QObject *parent = nullptr) : ObjectProperties(document, document->tileset().data(), parent) { - mNameProperty = propertyFactory->createProperty( + mNameProperty = new StringProperty( tr("Name"), - [this]() { + [this] { return tilesetDocument()->tileset()->name(); }, - [this](const QVariant &value) { - push(new RenameTileset(tilesetDocument(), value.toString())); + [this](const QString &value) { + push(new RenameTileset(tilesetDocument(), value)); }); - mObjectAlignmentProperty = propertyFactory->createProperty( + mObjectAlignmentProperty = new EnumProperty( tr("Object Alignment"), - [this]() { - return QVariant::fromValue(tileset()->objectAlignment()); + [this] { + return tileset()->objectAlignment(); }, - [this](const QVariant &value) { - const auto objectAlignment = static_cast(value.toInt()); - push(new ChangeTilesetObjectAlignment(tilesetDocument(), objectAlignment)); + [this](Alignment value) { + push(new ChangeTilesetObjectAlignment(tilesetDocument(), value)); }); - mTileOffsetProperty = propertyFactory->createProperty( + mTileOffsetProperty = new PointProperty( tr("Drawing Offset"), - [this]() { + [this] { return tileset()->tileOffset(); }, - [this](const QVariant &value) { - push(new ChangeTilesetTileOffset(tilesetDocument(), value.value())); + [this](const QPoint &value) { + push(new ChangeTilesetTileOffset(tilesetDocument(), value)); }); - mTileRenderSizeProperty = propertyFactory->createProperty( + mTileRenderSizeProperty = new EnumProperty( tr("Tile Render Size"), - [this]() { - return QVariant::fromValue(tileset()->tileRenderSize()); + [this] { + return tileset()->tileRenderSize(); }, - [this](const QVariant &value) { - const auto tileRenderSize = static_cast(value.toInt()); - push(new ChangeTilesetTileRenderSize(tilesetDocument(), tileRenderSize)); + [this](Tileset::TileRenderSize value) { + push(new ChangeTilesetTileRenderSize(tilesetDocument(), value)); }); - mFillModeProperty = propertyFactory->createProperty( + mFillModeProperty = new EnumProperty( tr("Fill Mode"), - [this]() { - return QVariant::fromValue(tileset()->fillMode()); + [this] { + return tileset()->fillMode(); }, - [this](const QVariant &value) { - const auto fillMode = static_cast(value.toInt()); - push(new ChangeTilesetFillMode(tilesetDocument(), fillMode)); + [this](Tileset::FillMode value) { + push(new ChangeTilesetFillMode(tilesetDocument(), value)); }); - mBackgroundColorProperty = propertyFactory->createProperty( + mBackgroundColorProperty = new ColorProperty( tr("Background Color"), - [this]() { + [this] { return tileset()->backgroundColor(); }, - [this](const QVariant &value) { - push(new ChangeTilesetBackgroundColor(tilesetDocument(), value.value())); + [this](const QColor &value) { + push(new ChangeTilesetBackgroundColor(tilesetDocument(), value)); }); - mOrientationProperty = propertyFactory->createProperty( + mOrientationProperty = new EnumProperty( tr("Orientation"), - [this]() { - return QVariant::fromValue(tileset()->orientation()); + [this] { + return tileset()->orientation(); }, - [this](const QVariant &value) { - const auto orientation = static_cast(value.toInt()); - push(new ChangeTilesetOrientation(tilesetDocument(), orientation)); + [this](Tileset::Orientation value) { + push(new ChangeTilesetOrientation(tilesetDocument(), value)); }); - mGridSizeProperty = propertyFactory->createProperty( + mGridSizeProperty = new SizeProperty( tr("Grid Size"), - [this]() { + [this] { return tileset()->gridSize(); }, - [this](const QVariant &value) { - push(new ChangeTilesetGridSize(tilesetDocument(), value.toSize())); + [this](const QSize &value) { + push(new ChangeTilesetGridSize(tilesetDocument(), value)); }); // todo: needs 1 as minimum value - mColumnCountProperty = propertyFactory->createProperty( + mColumnCountProperty = new IntProperty( tr("Columns"), - [this]() { + [this] { return tileset()->columnCount(); }, - [this](const QVariant &value) { - push(new ChangeTilesetColumnCount(tilesetDocument(), value.toInt())); + [this](const int &value) { + push(new ChangeTilesetColumnCount(tilesetDocument(), value)); }); // todo: this needs a custom widget - mAllowedTransformationsProperty = propertyFactory->createProperty( + mAllowedTransformationsProperty = new IntProperty( tr("Allowed Transformations"), - [this]() { - return QVariant::fromValue(tileset()->transformationFlags()); + [this] { + return static_cast(tileset()->transformationFlags()); }, - [this](const QVariant &value) { - const auto flags = static_cast(value.toInt()); + [this](const int &value) { + const auto flags = static_cast(value); push(new ChangeTilesetTransformationFlags(tilesetDocument(), flags)); }); // todo: this needs a custom widget - mImageProperty = propertyFactory->createProperty( + mImageProperty = new UrlProperty( tr("Image"), - [this]() { - return tileset()->imageSource().toString(); + [this] { + return tileset()->imageSource(); }, - [](const QVariant &) { + [](const QUrl &) { // push(new ChangeTilesetImage(tilesetDocument(), value.toString())); }); @@ -1058,62 +1174,62 @@ class MapObjectProperties : public ObjectProperties Q_OBJECT public: - MapObjectProperties(MapDocument *document, MapObject *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) + MapObjectProperties(MapDocument *document, MapObject *object, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { mIdProperty = new IntProperty( tr("ID"), - [this]() { return mapObject()->id(); }); + [this] { return mapObject()->id(); }); mIdProperty->setEnabled(false); mTemplateProperty = new UrlProperty( tr("Template"), - [this]() { + [this] { if (auto objectTemplate = mapObject()->objectTemplate()) return QUrl::fromLocalFile(objectTemplate->fileName()); return QUrl(); }); mTemplateProperty->setEnabled(false); - mNameProperty = propertyFactory->createProperty( + mNameProperty = new StringProperty( tr("Name"), - [this]() { + [this] { return mapObject()->name(); }, - [this](const QVariant &value) { + [this](const QString &value) { changeMapObject(MapObject::NameProperty, value); }); - mVisibleProperty = propertyFactory->createProperty( + mVisibleProperty = new BoolProperty( tr("Visible"), - [this]() { + [this] { return mapObject()->isVisible(); }, - [this](const QVariant &value) { + [this](const bool &value) { changeMapObject(MapObject::VisibleProperty, value); }); - mPositionProperty = propertyFactory->createProperty( + mPositionProperty = new PointFProperty( tr("Position"), - [this]() { + [this] { return mapObject()->position(); }, - [this](const QVariant &value) { + [this](const QPointF &value) { changeMapObject(MapObject::PositionProperty, value); }); - mSizeProperty = propertyFactory->createProperty( + mSizeProperty = new SizeFProperty( tr("Size"), - [this]() { + [this] { return mapObject()->size(); }, - [this](const QVariant &value) { + [this](const QSizeF &value) { changeMapObject(MapObject::SizeProperty, value); }); mRotationProperty = new FloatProperty( tr("Rotation"), - [this]() { + [this] { return mapObject()->rotation(); }, [this](const qreal &value) { @@ -1123,13 +1239,13 @@ class MapObjectProperties : public ObjectProperties mRotationProperty->setSuffix(QStringLiteral("°")); // todo: make this a custom widget with "Horizontal" and "Vertical" checkboxes - mFlippingProperty = propertyFactory->createProperty( + mFlippingProperty = new IntProperty( tr("Flipping"), - [this]() { + [this] { return mapObject()->cell().flags(); }, - [this](const QVariant &value) { - const int flippingFlags = value.toInt(); + [this](const int &value) { + const int flippingFlags = value; MapObjectCell mapObjectCell; mapObjectCell.object = mapObject(); @@ -1146,48 +1262,50 @@ class MapObjectProperties : public ObjectProperties push(command); }); - mTextProperty = propertyFactory->createProperty( + // todo: allow opening the multi-line text dialog + mTextProperty = new StringProperty( tr("Text"), - [this]() { + [this] { return mapObject()->textData().text; }, - [this](const QVariant &value) { + [this](const QString &value) { changeMapObject(MapObject::TextProperty, value); }); - mTextAlignmentProperty = propertyFactory->createProperty( + mTextAlignmentProperty = new QtAlignmentProperty( tr("Alignment"), - [this]() { - return QVariant::fromValue(mapObject()->textData().alignment); + [this] { + return mapObject()->textData().alignment; }, - [this](const QVariant &value) { - changeMapObject(MapObject::TextAlignmentProperty, value); + [this](const Qt::Alignment &value) { + changeMapObject(MapObject::TextAlignmentProperty, + QVariant::fromValue(value)); }); - mTextFontProperty = propertyFactory->createProperty( + mTextFontProperty = new FontProperty( tr("Font"), - [this]() { + [this] { return mapObject()->textData().font; }, - [this](const QVariant &value) { + [this](const QFont &value) { changeMapObject(MapObject::TextFontProperty, value); }); - mTextWordWrapProperty = propertyFactory->createProperty( + mTextWordWrapProperty = new BoolProperty( tr("Word Wrap"), - [this]() { + [this] { return mapObject()->textData().wordWrap; }, - [this](const QVariant &value) { + [this](const bool &value) { changeMapObject(MapObject::TextWordWrapProperty, value); }); - mTextColorProperty = propertyFactory->createProperty( + mTextColorProperty = new ColorProperty( tr("Text Color"), - [this]() { + [this] { return mapObject()->textData().color; }, - [this](const QVariant &value) { + [this](const QColor &value) { changeMapObject(MapObject::TextColorProperty, value); }); @@ -1320,42 +1438,41 @@ class TileProperties : public ObjectProperties Q_OBJECT public: - TileProperties(Document *document, Tile *object, PropertyFactory *propertyFactory, QObject *parent = nullptr) + TileProperties(Document *document, Tile *object, QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mIdProperty = propertyFactory->createProperty( + mIdProperty = new IntProperty( tr("ID"), - [this]() { return tile()->id(); }, - [](const QVariant &) {}); + [this] { return tile()->id(); }); mIdProperty->setEnabled(false); // todo: apply readableImageFormatsFilter - mImageProperty = propertyFactory->createProperty( + mImageProperty = new UrlProperty( tr("Image"), - [this]() { return tile()->imageSource(); }, - [this](const QVariant &value) { + [this] { return tile()->imageSource(); }, + [this](const QUrl &value) { push(new ChangeTileImageSource(tilesetDocument(), tile(), - value.toUrl())); + value)); }); - mRectangleProperty = propertyFactory->createProperty( + mRectangleProperty = new RectProperty( tr("Rectangle"), - [this]() { return tile()->imageRect(); }, - [this](const QVariant &value) { + [this] { return tile()->imageRect(); }, + [this](const QRect &value) { push(new ChangeTileImageRect(tilesetDocument(), { tile() }, - { value.toRect() })); + { value })); }); // todo: minimum value should be 0 - mProbabilityProperty = propertyFactory->createProperty( + mProbabilityProperty = new FloatProperty( tr("Probability"), - [this]() { return tile()->probability(); }, - [this](const QVariant &value) { + [this] { return tile()->probability(); }, + [this](const double &value) { push(new ChangeTileProbability(tilesetDocument(), tilesetDocument()->selectedTiles(), - value.toReal())); + value)); }); mProbabilityProperty->setToolTip(tr("Relative chance this tile will be picked")); @@ -1439,31 +1556,31 @@ class WangSetProperties : public ObjectProperties public: WangSetProperties(Document *document, WangSet *object, - PropertyFactory *propertyFactory, QObject *parent = nullptr) + QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mNameProperty = propertyFactory->createProperty( + mNameProperty = new StringProperty( tr("Name"), - [this]() { return wangSet()->name(); }, - [this](const QVariant &value) { - push(new RenameWangSet(tilesetDocument(), wangSet(), value.toString())); + [this] { return wangSet()->name(); }, + [this](const QString &value) { + push(new RenameWangSet(tilesetDocument(), wangSet(), value)); }); - mTypeProperty = propertyFactory->createProperty( + mTypeProperty = new EnumProperty( tr("Type"), - [this]() { return QVariant::fromValue(wangSet()->type()); }, - [this](const QVariant &value) { - push(new ChangeWangSetType(tilesetDocument(), wangSet(), static_cast(value.toInt()))); + [this] { return wangSet()->type(); }, + [this](WangSet::Type value) { + push(new ChangeWangSetType(tilesetDocument(), wangSet(), value)); }); // todo: keep between 0 and WangId::MAX_COLOR_COUNT - mColorCountProperty = propertyFactory->createProperty( + mColorCountProperty = new IntProperty( tr("Color Count"), - [this]() { return wangSet()->colorCount(); }, - [this](const QVariant &value) { + [this] { return wangSet()->colorCount(); }, + [this](const int &value) { push(new ChangeWangSetColorCount(tilesetDocument(), wangSet(), - value.toInt())); + value)); }); connect(document, &Document::changed, @@ -1536,29 +1653,29 @@ class WangColorProperties : public ObjectProperties public: WangColorProperties(Document *document, WangColor *object, - PropertyFactory *propertyFactory, QObject *parent = nullptr) + QObject *parent = nullptr) : ObjectProperties(document, object, parent) { - mNameProperty = propertyFactory->createProperty( + mNameProperty = new StringProperty( tr("Name"), - [this]() { return wangColor()->name(); }, - [this](const QVariant &value) { - push(new ChangeWangColorName(tilesetDocument(), wangColor(), value.toString())); + [this] { return wangColor()->name(); }, + [this](const QString &value) { + push(new ChangeWangColorName(tilesetDocument(), wangColor(), value)); }); - mColorProperty = propertyFactory->createProperty( + mColorProperty = new ColorProperty( tr("Color"), - [this]() { return wangColor()->color(); }, - [this](const QVariant &value) { - push(new ChangeWangColorColor(tilesetDocument(), wangColor(), value.value())); + [this] { return wangColor()->color(); }, + [this](const QColor &value) { + push(new ChangeWangColorColor(tilesetDocument(), wangColor(), value)); }); // todo: set 0.01 as minimum - mProbabilityProperty = propertyFactory->createProperty( + mProbabilityProperty = new FloatProperty( tr("Probability"), - [this]() { return wangColor()->probability(); }, - [this](const QVariant &value) { - push(new ChangeWangColorProbability(tilesetDocument(), wangColor(), value.toReal())); + [this] { return wangColor()->probability(); }, + [this](const double &value) { + push(new ChangeWangColorProbability(tilesetDocument(), wangColor(), value)); }); connect(document, &Document::changed, @@ -1643,48 +1760,48 @@ void PropertiesWidget::currentObjectChanged(Object *object) case Layer::ImageLayerType: mPropertiesObject = new ImageLayerProperties(mapDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; case Layer::ObjectGroupType: mPropertiesObject = new ObjectGroupProperties(mapDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; case Layer::TileLayerType: case Layer::GroupLayerType: mPropertiesObject = new LayerProperties(mapDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; } break; } case Object::MapObjectType: mPropertiesObject = new MapObjectProperties(static_cast(mDocument), - static_cast(object), mPropertyFactory.get(), this); + static_cast(object), this); break; case Object::MapType: mPropertiesObject = new MapProperties(static_cast(mDocument), - mPropertyFactory.get(), this); + this); break; case Object::TilesetType: mPropertiesObject = new TilesetProperties(static_cast(mDocument), - mPropertyFactory.get(), this); + this); break; case Object::TileType: mPropertiesObject = new TileProperties(mDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; case Object::WangSetType: mPropertiesObject = new WangSetProperties(mDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; case Object::WangColorType: mPropertiesObject = new WangColorProperties(mDocument, static_cast(object), - mPropertyFactory.get(), this); + this); break; case Object::ProjectType: case Object::WorldType: @@ -2076,136 +2193,6 @@ void PropertiesWidget::keyPressEvent(QKeyEvent *event) } } -void PropertiesWidget::registerEditorFactories() -{ - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Unspecified"), - tr("Top Left"), - tr("Top"), - tr("Top Right"), - tr("Left"), - tr("Center"), - tr("Right"), - tr("Bottom Left"), - tr("Bottom"), - tr("Bottom Right"), - })); - - // We leave out the "Unknown" orientation, because it shouldn't occur here - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Orthogonal"), - tr("Isometric"), - tr("Isometric (Staggered)"), - tr("Hexagonal (Staggered)"), - }, - QList { - Map::Orthogonal, - Map::Isometric, - Map::Staggered, - Map::Hexagonal, - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("X"), - tr("Y"), - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Odd"), - tr("Even"), - })); - - QStringList layerFormatNames = { - QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), - QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), - }; - QList layerFormatValues = { - Map::XML, - Map::Base64, - Map::Base64Gzip, - Map::Base64Zlib, - }; - - if (compressionSupported(Zstandard)) { - layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); - layerFormatValues.append(Map::Base64Zstandard); - } - - layerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); - layerFormatValues.append(Map::CSV); - - registerEditorFactory(qMetaTypeId(), - std::make_unique(layerFormatNames, layerFormatValues)); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Right Down"), - tr("Right Up"), - tr("Left Down"), - tr("Left Up"), - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Orthogonal"), - tr("Isometric"), - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Tile Size"), - tr("Map Grid Size"), - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Stretch"), - tr("Preserve Aspect Ratio"), - })); - - registerEditorFactory(qMetaTypeId(), - std::make_unique( - QStringList { - tr("Top Down"), - tr("Index Order"), - })); - - auto wangSetTypeEditorFactory = std::make_unique( - QStringList { - tr("Corner"), - tr("Edge"), - tr("Mixed"), - }); - - QMap mWangSetIcons; - mWangSetIcons.insert(WangSet::Corner, wangSetIcon(WangSet::Corner)); - mWangSetIcons.insert(WangSet::Edge, wangSetIcon(WangSet::Edge)); - mWangSetIcons.insert(WangSet::Mixed, wangSetIcon(WangSet::Mixed)); - wangSetTypeEditorFactory->setEnumIcons(mWangSetIcons); - - registerEditorFactory(qMetaTypeId(), - std::move(wangSetTypeEditorFactory)); -} - -void PropertiesWidget::registerEditorFactory(int type, std::unique_ptr factory) -{ - mPropertyFactory->registerEditorFactory(type, std::move(factory)); -} - void PropertiesWidget::retranslateUi() { mActionAddProperty->setText(QCoreApplication::translate("Tiled::PropertiesDock", "Add Property")); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index c3a2bb0088..443529e38a 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -22,16 +22,12 @@ #include -#include - namespace Tiled { class Object; class Document; -class EditorFactory; class ObjectProperties; -class PropertyFactory; class VariantEditor; /** @@ -63,9 +59,6 @@ public slots: void keyPressEvent(QKeyEvent *event) override; private: - void registerEditorFactories(); - void registerEditorFactory(int type, std::unique_ptr factory); - void currentObjectChanged(Object *object); void updateActions(); @@ -84,7 +77,6 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; VariantEditor *mPropertyBrowser; - std::unique_ptr mPropertyFactory; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index f73b582fb0..38d9629d0e 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -36,45 +36,19 @@ #include #include #include -#include namespace Tiled { -AbstractProperty::AbstractProperty(const QString &name, - EditorFactory *editorFactory, - QObject *parent) - : Property(name, parent) - , m_editorFactory(editorFactory) -{} - -QWidget *AbstractProperty::createEditor(QWidget *parent) -{ - return m_editorFactory ? m_editorFactory->createEditor(this, parent) - : nullptr; -} - - -GetSetProperty::GetSetProperty(const QString &name, - std::function get, - std::function set, - EditorFactory *editorFactory, - QObject *parent) - : AbstractProperty(name, editorFactory, parent) - , m_get(std::move(get)) - , m_set(std::move(set)) -{} - - QWidget *StringProperty::createEditor(QWidget *parent) { auto editor = new QLineEdit(parent); auto syncEditor = [=] { - editor->setText(m_get()); + editor->setText(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &QLineEdit::textEdited, this, m_set); + QObject::connect(editor, &QLineEdit::textEdited, this, &StringProperty::setValue); return editor; } @@ -85,12 +59,12 @@ QWidget *UrlProperty::createEditor(QWidget *parent) editor->setFilter(m_filter); auto syncEditor = [=] { - editor->setFileUrl(m_get()); + editor->setFileUrl(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &FileEdit::fileUrlChanged, this, m_set); + QObject::connect(editor, &FileEdit::fileUrlChanged, this, &UrlProperty::setValue); return editor; } @@ -100,13 +74,13 @@ QWidget *IntProperty::createEditor(QWidget *parent) auto editor = new SpinBox(parent); auto syncEditor = [=] { const QSignalBlocker blocker(editor); - editor->setValue(m_get()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, qOverload(&SpinBox::valueChanged), - this, m_set); + this, &IntProperty::setValue); return editor; } @@ -118,13 +92,13 @@ QWidget *FloatProperty::createEditor(QWidget *parent) auto syncEditor = [=] { const QSignalBlocker blocker(editor); - editor->setValue(m_get()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), - this, m_set); + this, &FloatProperty::setValue); return editor; } @@ -134,7 +108,7 @@ QWidget *BoolProperty::createEditor(QWidget *parent) auto editor = new QCheckBox(parent); auto syncEditor = [=] { const QSignalBlocker blocker(editor); - bool checked = m_get(); + bool checked = value(); editor->setChecked(checked); editor->setText(checked ? tr("On") : tr("Off")); }; @@ -143,7 +117,7 @@ QWidget *BoolProperty::createEditor(QWidget *parent) QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &QCheckBox::toggled, this, [=](bool checked) { editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); - m_set(checked); + setValue(checked); }); return editor; @@ -154,14 +128,14 @@ QWidget *PointProperty::createEditor(QWidget *parent) auto editor = new PointEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(m_get()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &PointEdit::valueChanged, this, [this, editor] { - m_set(editor->value()); + setValue(editor->value()); }); return editor; @@ -172,14 +146,14 @@ QWidget *PointFProperty::createEditor(QWidget *parent) auto editor = new PointFEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(this->value().toPointF()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &PointFEdit::valueChanged, this, [this, editor] { - this->setValue(editor->value()); + this->setVariantValue(editor->value()); }); return editor; @@ -190,14 +164,14 @@ QWidget *SizeProperty::createEditor(QWidget *parent) auto editor = new SizeEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(m_get()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &SizeEdit::valueChanged, this, [this, editor] { - m_set(editor->value()); + setValue(editor->value()); }); return editor; @@ -208,14 +182,14 @@ QWidget *SizeFProperty::createEditor(QWidget *parent) auto editor = new SizeFEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(this->value().toSizeF()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &SizeFEdit::valueChanged, this, [this, editor] { - this->setValue(editor->value()); + setValue(editor->value()); }); return editor; @@ -226,15 +200,15 @@ QWidget *RectProperty::createEditor(QWidget *parent) auto editor = new RectEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(this->value().toRect()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &RectEdit::valueChanged, this, [this, editor] { - this->setValue(editor->value()); - }); + setValue(editor->value()); + }); return editor; } @@ -244,14 +218,14 @@ QWidget *RectFProperty::createEditor(QWidget *parent) auto editor = new RectFEdit(parent); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setValue(this->value().toRectF()); + editor->setValue(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &RectFEdit::valueChanged, this, [this, editor] { - this->setValue(editor->value()); + setValue(editor->value()); }); return editor; @@ -263,14 +237,14 @@ QWidget *ColorProperty::createEditor(QWidget *parent) auto editor = new ColorButton(parent); auto syncEditor = [=] { const QSignalBlocker blocker(editor); - editor->setColor(this->value().value()); + editor->setColor(value()); }; syncEditor(); QObject::connect(this, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, &ColorButton::colorChanged, this, [this, editor] { - this->setValue(editor->color()); + setValue(editor->color()); }); return editor; @@ -301,7 +275,7 @@ QWidget *FontProperty::createEditor(QWidget *parent) layout->addWidget(kerningCheckBox); auto syncEditor = [=] { - const auto font = this->value().value(); + const auto font = value(); const QSignalBlocker fontBlocker(fontComboBox); const QSignalBlocker sizeBlocker(sizeSpinBox); const QSignalBlocker boldBlocker(boldCheckBox); @@ -326,7 +300,7 @@ QWidget *FontProperty::createEditor(QWidget *parent) font.setUnderline(underlineCheckBox->isChecked()); font.setStrikeOut(strikeoutCheckBox->isChecked()); font.setKerning(kerningCheckBox->isChecked()); - this->setValue(font); + setValue(font); }; syncEditor(); @@ -343,7 +317,7 @@ QWidget *FontProperty::createEditor(QWidget *parent) return editor; } -QWidget *AlignmentProperty::createEditor(QWidget *parent) +QWidget *QtAlignmentProperty::createEditor(QWidget *parent) { auto editor = new QWidget(parent); auto layout = new QGridLayout(editor); @@ -374,15 +348,14 @@ QWidget *AlignmentProperty::createEditor(QWidget *parent) auto syncEditor = [=] { const QSignalBlocker horizontalBlocker(horizontalComboBox); const QSignalBlocker verticalBlocker(verticalComboBox); - const auto alignment = this->value().value(); + const auto alignment = value(); horizontalComboBox->setCurrentIndex(horizontalComboBox->findData(static_cast(alignment & Qt::AlignHorizontal_Mask))); verticalComboBox->setCurrentIndex(verticalComboBox->findData(static_cast(alignment & Qt::AlignVertical_Mask))); }; auto syncProperty = [=] { - const Qt::Alignment alignment(horizontalComboBox->currentData().toInt() | - verticalComboBox->currentData().toInt()); - this->setValue(QVariant::fromValue(alignment)); + setValue(Qt::Alignment(horizontalComboBox->currentData().toInt() | + verticalComboBox->currentData().toInt())); }; syncEditor(); @@ -395,39 +368,6 @@ QWidget *AlignmentProperty::createEditor(QWidget *parent) } -ValueProperty::ValueProperty(const QString &name, - const QVariant &value, - EditorFactory *editorFactory, - QObject *parent) - : AbstractProperty(name, editorFactory, parent) - , m_value(value) -{} - -void ValueProperty::setValue(const QVariant &value) -{ - if (m_value != value) { - m_value = value; - emit valueChanged(); - } -} - - -EnumProperty::EnumProperty(const QString &name, - QObject *parent) - : AbstractProperty(name, &m_editorFactory, parent) -{} - -void EnumProperty::setEnumNames(const QStringList &enumNames) -{ - m_editorFactory.setEnumNames(enumNames); -} - -void EnumProperty::setEnumValues(const QList &enumValues) -{ - m_editorFactory.setEnumValues(enumValues); -} - - VariantEditor::VariantEditor(QWidget *parent) : QScrollArea(parent) { @@ -567,70 +507,45 @@ QWidget *VariantEditor::createEditor(Property *property) } -EnumEditorFactory::EnumEditorFactory(const QStringList &enumNames, - const QList &enumValues) - : m_enumNamesModel(enumNames) - , m_enumValues(enumValues) -{} - -void EnumEditorFactory::setEnumNames(const QStringList &enumNames) -{ - m_enumNamesModel.setStringList(enumNames); -} - -void EnumEditorFactory::setEnumIcons(const QMap &enumIcons) -{ - // todo: add support for showing these icons in the QComboBox - m_enumIcons = enumIcons; -} - -void EnumEditorFactory::setEnumValues(const QList &enumValues) -{ - m_enumValues = enumValues; -} - -QWidget *EnumEditorFactory::createEditor(Property *property, QWidget *parent) +QWidget *createEnumEditor(IntProperty *property, const EnumData &enumData, QWidget *parent) { auto editor = new QComboBox(parent); // This allows the combo box to shrink horizontally. editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); - editor->setModel(&m_enumNamesModel); - auto syncEditor = [property, editor, this] { + for (qsizetype i = 0; i < enumData.names.size(); ++i) { + auto value = enumData.values.isEmpty() ? i : enumData.values.value(i); + editor->addItem(enumData.icons[value], + enumData.names[i], + value); + } + + auto syncEditor = [property, editor] { const QSignalBlocker blocker(editor); - if (m_enumValues.isEmpty()) - editor->setCurrentIndex(property->value().toInt()); - else - editor->setCurrentIndex(m_enumValues.indexOf(property->value().toInt())); + editor->setCurrentIndex(editor->findData(property->value())); }; syncEditor(); QObject::connect(property, &Property::valueChanged, editor, syncEditor); QObject::connect(editor, qOverload(&QComboBox::currentIndexChanged), property, - [property, this](int index) { - property->setValue(m_enumValues.isEmpty() ? index : m_enumValues.at(index)); + [editor, property] { + property->setValue(editor->currentData().toInt()); }); return editor; } - -void PropertyFactory::registerEditorFactory(int type, std::unique_ptr factory) -{ - m_factories[type] = std::move(factory); -} - Property *PropertyFactory::createQObjectProperty(QObject *qObject, - const char *name, + const char *propertyName, const QString &displayName) { auto metaObject = qObject->metaObject(); - auto propertyIndex = metaObject->indexOfProperty(name); + auto propertyIndex = metaObject->indexOfProperty(propertyName); if (propertyIndex < 0) return nullptr; auto metaProperty = metaObject->property(propertyIndex); - auto property = createProperty(displayName.isEmpty() ? QString::fromUtf8(name) + auto property = createProperty(displayName.isEmpty() ? QString::fromUtf8(propertyName) : displayName, [=] { return metaProperty.read(qObject); @@ -655,15 +570,6 @@ Property *PropertyFactory::createQObjectProperty(QObject *qObject, return property; } -ValueProperty *PropertyFactory::createProperty(const QString &name, - const QVariant &value) -{ - auto f = m_factories.find(value.userType()); - return new ValueProperty(name, value, - f != m_factories.end() ? f->second.get() - : nullptr); -} - template Property *createTypedProperty(const QString &name, std::function get, @@ -708,14 +614,10 @@ Property *PropertyFactory::createProperty(const QString &name, return createTypedProperty(name, get, set); default: if (type == qMetaTypeId()) - return createTypedProperty(name, get, set); + return createTypedProperty(name, get, set); } - // Fall back to registered factories approach (still used for enums) - auto f = m_factories.find(get().userType()); - return new GetSetProperty(name, get, set, - f != m_factories.end() ? f->second.get() - : nullptr); + return nullptr; } } // namespace Tiled diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 7c257b9bc8..8fdb125ea2 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -28,9 +28,6 @@ #include #include -#include -#include - class QGridLayout; namespace Tiled { @@ -43,7 +40,7 @@ class Property : public QObject Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) - Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(QVariant value READ variantValue WRITE setVariantValue NOTIFY valueChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) public: @@ -72,8 +69,8 @@ class Property : public QObject } } - virtual QVariant value() const = 0; - virtual void setValue(const QVariant &value) = 0; + virtual QVariant variantValue() const = 0; + virtual void setVariantValue(const QVariant &value) = 0; virtual QWidget *createEditor(QWidget *parent) = 0; @@ -106,18 +103,21 @@ class PropertyTemplate : public Property , m_set(std::move(set)) {} - QVariant value() const override + Type value() const { return m_get(); } + void setValue(const Type &value) { m_set(value); } + + QVariant variantValue() const override { return QVariant::fromValue(m_get()); } - void setValue(const QVariant &value) override + void setVariantValue(const QVariant &value) override { if (m_set) m_set(value.value()); } -protected: +private: std::function m_get; std::function m_set; }; @@ -207,115 +207,12 @@ struct FontProperty : PropertyTemplate QWidget *createEditor(QWidget *parent) override; }; -struct AlignmentProperty : PropertyTemplate +struct QtAlignmentProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; }; -/** - * An editor factory is responsible for creating an editor widget for a given - * property. It can be used to share the configuration of editor widgets - * between different properties. - */ -class EditorFactory -{ - Q_DECLARE_TR_FUNCTIONS(EditorFactory) - -public: - virtual QWidget *createEditor(Property *property, QWidget *parent) = 0; -}; - -/** - * An editor factory that creates a combo box for enum properties. - */ -class EnumEditorFactory : public EditorFactory -{ -public: - EnumEditorFactory(const QStringList &enumNames = {}, - const QList &enumValues = {}); - - void setEnumNames(const QStringList &enumNames); - void setEnumIcons(const QMap &enumIcons); - void setEnumValues(const QList &enumValues); - - QWidget *createEditor(Property *property, QWidget *parent) override; - -private: - QStringListModel m_enumNamesModel; - QMap m_enumIcons; - QList m_enumValues; -}; - -/** - * A property that uses an editor factory to create its editor, but does not - * store a value itself. - * - * The property does not take ownership of the editor factory. - */ -class AbstractProperty : public Property -{ - Q_OBJECT - -public: - AbstractProperty(const QString &name, - EditorFactory *editorFactory, - QObject *parent = nullptr); - - QWidget *createEditor(QWidget *parent) override; - -private: - EditorFactory *m_editorFactory; -}; - -/** - * A property that uses the given functions to get and set the value and uses - * an editor factory to create its editor. - * - * The property does not take ownership of the editor factory. - */ -class GetSetProperty : public AbstractProperty -{ - Q_OBJECT - -public: - GetSetProperty(const QString &name, - std::function get, - std::function set, - EditorFactory *editorFactory, - QObject *parent = nullptr); - - QVariant value() const override { return m_get(); } - void setValue(const QVariant &value) override { m_set(value); } - -private: - std::function m_get; - std::function m_set; -}; - -/** - * A property that stores a value of a given type and uses an editor factory to - * create its editor. - * - * The property does not take ownership of the editor factory. - */ -class ValueProperty : public AbstractProperty -{ - Q_OBJECT - -public: - ValueProperty(const QString &name, - const QVariant &value, - EditorFactory *editorFactory, - QObject *parent = nullptr); - - QVariant value() const override { return m_value; } - void setValue(const QVariant &value) override; - -private: - QVariant m_value; -}; - /** * A property factory that instantiates the appropriate property type based on @@ -326,57 +223,81 @@ class PropertyFactory public: PropertyFactory() = default; - /** - * Register an editor factory for a given type. - * - * When there is already an editor factory registered for the given type, - * it will be replaced. - */ - void registerEditorFactory(int type, std::unique_ptr factory); - /** * Creates a property that wraps a QObject property. */ Property *createQObjectProperty(QObject *qObject, - const char *name, + const char *propertyName, const QString &displayName = {}); - /** - * Creates a property with the given name and value. The property will use - * the editor factory registered for the type of the value. - */ - ValueProperty *createProperty(const QString &name, const QVariant &value); - /** * Creates a property with the given name and get/set functions. The - * property will use the editor factory registered for the type of the - * value. + * value type determines the kind of property that will be created. */ Property *createProperty(const QString &name, std::function get, std::function set); +}; -private: - std::unordered_map> m_factories; +struct EnumData +{ + EnumData(const QStringList &names, + const QList &values = {}, + const QMap &icons = {}) + : names(names) + , values(values) + , icons(icons) + {} + + QStringList names; + QList values; // optional + QMap icons; // optional }; +template +EnumData enumData() +{ + return {{}}; +} + +QWidget *createEnumEditor(IntProperty *property, + const EnumData &enumData, + QWidget *parent); + /** - * A property that wraps an enum value and uses an editor factory to create - * its editor. + * A property that wraps an enum value and creates a combo box based on the + * given EnumData. */ -class EnumProperty : public AbstractProperty +template +class EnumProperty : public IntProperty { - Q_OBJECT - public: EnumProperty(const QString &name, - QObject *parent = nullptr); + std::function get, + std::function set, + QObject *parent = nullptr) + : IntProperty(name, + [get] { + return static_cast(get()); + }, + set ? [set](const int &value){ set(static_cast(value)); } + : std::function(), + parent) + , m_enumData(enumData()) + {} - void setEnumNames(const QStringList &enumNames); - void setEnumValues(const QList &enumValues); + void setEnumData(const EnumData &enumData) + { + m_enumData = enumData; + } + + QWidget *createEditor(QWidget *parent) override + { + return createEnumEditor(this, m_enumData, parent); + } private: - EnumEditorFactory m_editorFactory; + EnumData m_enumData; }; From dc083df222cc63891e17572b5014209f8eaeee6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 9 Sep 2024 16:56:00 +0200 Subject: [PATCH 17/78] Addressed various property todo items * Apply various minimum and maximum values * Set custom step size where needed * Set file filters where appropriate * Support constraint on RectProperty --- src/tiled/propertieswidget.cpp | 51 ++++++------ src/tiled/propertyeditorwidgets.cpp | 32 ++++++++ src/tiled/propertyeditorwidgets.h | 6 ++ src/tiled/textpropertyedit.h | 1 + src/tiled/varianteditor.cpp | 119 ++++++++++++++++++---------- src/tiled/varianteditor.h | 56 +++++++++++++ 6 files changed, 198 insertions(+), 67 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index e97b0dd15b..0bdb287d6c 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -472,6 +472,7 @@ class MapProperties : public ObjectProperties } }, this); + mTileSizeProperty->setMinimum(1); mInfiniteProperty = new BoolProperty( tr("Infinite"), @@ -539,6 +540,7 @@ class MapProperties : public ObjectProperties [this](const QSize &value) { push(new ChangeMapProperty(mapDocument(), value)); }); + mChunkSizeProperty->setMinimum(CHUNK_SIZE_MIN); mRenderOrderProperty = new EnumProperty( tr("Tile Render Order"), @@ -682,14 +684,14 @@ class MapProperties : public ObjectProperties Property *mOrientationProperty; Property *mSizeProperty; - Property *mTileSizeProperty; + SizeProperty *mTileSizeProperty; Property *mInfiniteProperty; Property *mHexSideLengthProperty; Property *mStaggerAxisProperty; Property *mStaggerIndexProperty; Property *mParallaxOriginProperty; Property *mLayerDataFormatProperty; - Property *mChunkSizeProperty; + SizeProperty *mChunkSizeProperty; Property *mRenderOrderProperty; Property *mCompressionLevelProperty; Property *mBackgroundColorProperty; @@ -733,14 +735,15 @@ class LayerProperties : public ObjectProperties push(new SetLayerLocked(mapDocument(), { layer() }, value)); }); - // todo: value should be between 0 and 1, and would be nice to use a slider (replacing the one in Layers view) - // todo: singleStep should be 0.1 + // todo: would be nice to use a slider (replacing the one in Layers view) mOpacityProperty = new FloatProperty( tr("Opacity"), [this] { return layer()->opacity(); }, [this](const double &value) { push(new SetLayerOpacity(mapDocument(), { layer() }, value)); }); + mOpacityProperty->setRange(0.0, 1.0); + mOpacityProperty->setSingleStep(0.1); mTintColorProperty = new ColorProperty( tr("Tint Color"), @@ -756,13 +759,13 @@ class LayerProperties : public ObjectProperties push(new SetLayerOffset(mapDocument(), { layer() }, value)); }); - // todo: singleStep should be 0.1 mParallaxFactorProperty = new PointFProperty( tr("Parallax Factor"), [this] { return layer()->parallaxFactor(); }, [this](const QPointF &value) { push(new SetLayerParallaxFactor(mapDocument(), { layer() }, value)); }); + mParallaxFactorProperty->setSingleStep(0.1); connect(document, &Document::changed, this, &LayerProperties::onChanged); @@ -821,10 +824,10 @@ class LayerProperties : public ObjectProperties Property *mNameProperty; Property *mVisibleProperty; Property *mLockedProperty; - Property *mOpacityProperty; + FloatProperty *mOpacityProperty; Property *mTintColorProperty; Property *mOffsetProperty; - Property *mParallaxFactorProperty; + PointFProperty *mParallaxFactorProperty; }; class ImageLayerProperties : public LayerProperties @@ -835,13 +838,13 @@ class ImageLayerProperties : public LayerProperties ImageLayerProperties(MapDocument *document, ImageLayer *object, QObject *parent = nullptr) : LayerProperties(document, object, parent) { - // todo: set a file filter for selecting images (or map files?) mImageProperty = new UrlProperty( tr("Image Source"), [this] { return imageLayer()->imageSource(); }, [this](const QUrl &value) { push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value)); }); + mImageProperty->setFilter(Utils::readableImageFormatsFilter()); mTransparentColorProperty = new ColorProperty( tr("Transparent Color"), @@ -904,7 +907,7 @@ class ImageLayerProperties : public LayerProperties return static_cast(mObject); } - Property *mImageProperty; + UrlProperty *mImageProperty; Property *mTransparentColorProperty; Property *mRepeatXProperty; Property *mRepeatYProperty; @@ -1047,8 +1050,8 @@ class TilesetProperties : public ObjectProperties [this](const QSize &value) { push(new ChangeTilesetGridSize(tilesetDocument(), value)); }); + mGridSizeProperty->setMinimum(1); - // todo: needs 1 as minimum value mColumnCountProperty = new IntProperty( tr("Columns"), [this] { @@ -1057,6 +1060,7 @@ class TilesetProperties : public ObjectProperties [this](const int &value) { push(new ChangeTilesetColumnCount(tilesetDocument(), value)); }); + mColumnCountProperty->setMinimum(1); // todo: this needs a custom widget mAllowedTransformationsProperty = new IntProperty( @@ -1163,8 +1167,8 @@ class TilesetProperties : public ObjectProperties Property *mFillModeProperty; Property *mBackgroundColorProperty; Property *mOrientationProperty; - Property *mGridSizeProperty; - Property *mColumnCountProperty; + SizeProperty *mGridSizeProperty; + IntProperty *mColumnCountProperty; Property *mAllowedTransformationsProperty; Property *mImageProperty; }; @@ -1262,8 +1266,7 @@ class MapObjectProperties : public ObjectProperties push(command); }); - // todo: allow opening the multi-line text dialog - mTextProperty = new StringProperty( + mTextProperty = new MultilineStringProperty( tr("Text"), [this] { return mapObject()->textData().text; @@ -1446,7 +1449,6 @@ class TileProperties : public ObjectProperties [this] { return tile()->id(); }); mIdProperty->setEnabled(false); - // todo: apply readableImageFormatsFilter mImageProperty = new UrlProperty( tr("Image"), [this] { return tile()->imageSource(); }, @@ -1455,6 +1457,7 @@ class TileProperties : public ObjectProperties tile(), value)); }); + mImageProperty->setFilter(Utils::readableImageFormatsFilter()); mRectangleProperty = new RectProperty( tr("Rectangle"), @@ -1464,8 +1467,8 @@ class TileProperties : public ObjectProperties { tile() }, { value })); }); + mRectangleProperty->setConstraint(object->image().rect()); - // todo: minimum value should be 0 mProbabilityProperty = new FloatProperty( tr("Probability"), [this] { return tile()->probability(); }, @@ -1475,6 +1478,7 @@ class TileProperties : public ObjectProperties value)); }); mProbabilityProperty->setToolTip(tr("Relative chance this tile will be picked")); + mProbabilityProperty->setMinimum(0.0); // annoying... maybe we should somehow always have the relevant TilesetDocument if (auto tilesetDocument = qobject_cast(document)) { @@ -1513,6 +1517,7 @@ class TileProperties : public ObjectProperties { if (tile != this->tile()) return; + mRectangleProperty->setConstraint(tile->image().rect()); emit mImageProperty->valueChanged(); emit mRectangleProperty->valueChanged(); } @@ -1545,9 +1550,9 @@ class TileProperties : public ObjectProperties } Property *mIdProperty; - Property *mImageProperty; - Property *mRectangleProperty; - Property *mProbabilityProperty; + UrlProperty *mImageProperty; + RectProperty *mRectangleProperty; + FloatProperty *mProbabilityProperty; }; class WangSetProperties : public ObjectProperties @@ -1573,7 +1578,6 @@ class WangSetProperties : public ObjectProperties push(new ChangeWangSetType(tilesetDocument(), wangSet(), value)); }); - // todo: keep between 0 and WangId::MAX_COLOR_COUNT mColorCountProperty = new IntProperty( tr("Color Count"), [this] { return wangSet()->colorCount(); }, @@ -1582,6 +1586,7 @@ class WangSetProperties : public ObjectProperties wangSet(), value)); }); + mColorCountProperty->setRange(0, WangId::MAX_COLOR_COUNT); connect(document, &Document::changed, this, &WangSetProperties::onChanged); @@ -1644,7 +1649,7 @@ class WangSetProperties : public ObjectProperties Property *mNameProperty; Property *mTypeProperty; - Property *mColorCountProperty; + IntProperty *mColorCountProperty; }; class WangColorProperties : public ObjectProperties @@ -1670,13 +1675,13 @@ class WangColorProperties : public ObjectProperties push(new ChangeWangColorColor(tilesetDocument(), wangColor(), value)); }); - // todo: set 0.01 as minimum mProbabilityProperty = new FloatProperty( tr("Probability"), [this] { return wangColor()->probability(); }, [this](const double &value) { push(new ChangeWangColorProbability(tilesetDocument(), wangColor(), value)); }); + mProbabilityProperty->setMinimum(0.01); connect(document, &Document::changed, this, &WangColorProperties::onChanged); @@ -1740,7 +1745,7 @@ class WangColorProperties : public ObjectProperties Property *mNameProperty; Property *mColorProperty; - Property *mProbabilityProperty; + FloatProperty *mProbabilityProperty; }; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 848652b8d4..706a91ab48 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -214,6 +214,12 @@ QSize SizeEdit::value() const m_heightSpinBox->value()); } +void SizeEdit::setMinimum(int minimum) +{ + m_widthSpinBox->setMinimum(minimum); + m_heightSpinBox->setMinimum(minimum); +} + SizeFEdit::SizeFEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) @@ -301,6 +307,12 @@ QPointF PointFEdit::value() const m_ySpinBox->value()); } +void PointFEdit::setSingleStep(double step) +{ + m_xSpinBox->setSingleStep(step); + m_ySpinBox->setSingleStep(step); +} + RectEdit::RectEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) @@ -320,6 +332,9 @@ RectEdit::RectEdit(QWidget *parent) { m_heightLabel, m_heightSpinBox }, }); + m_widthSpinBox->setMinimum(0); + m_heightSpinBox->setMinimum(0); + connect(m_xSpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); connect(m_ySpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &RectEdit::valueChanged); @@ -342,6 +357,23 @@ QRect RectEdit::value() const m_heightSpinBox->value()); } +void RectEdit::setConstraint(const QRect &constraint) +{ + if (constraint.isNull()) { + m_xSpinBox->setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + m_ySpinBox->setRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + m_widthSpinBox->setRange(0, std::numeric_limits::max()); + m_heightSpinBox->setRange(0, std::numeric_limits::max()); + } else { + m_xSpinBox->setRange(constraint.left(), constraint.right() + 1); + m_ySpinBox->setRange(constraint.top(), constraint.bottom() + 1); + m_widthSpinBox->setRange(0, constraint.width()); + m_heightSpinBox->setRange(0, constraint.height()); + } +} + RectFEdit::RectFEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index dc8c9417f0..84062ad323 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -84,6 +84,8 @@ class SizeEdit : public ResponsivePairswiseWidget void setValue(const QSize &size); QSize value() const; + void setMinimum(int minimum); + signals: void valueChanged(); @@ -156,6 +158,8 @@ class PointFEdit : public ResponsivePairswiseWidget void setValue(const QPointF &size); QPointF value() const; + void setSingleStep(double step); + signals: void valueChanged(); @@ -180,6 +184,8 @@ class RectEdit : public ResponsivePairswiseWidget void setValue(const QRect &size); QRect value() const; + void setConstraint(const QRect &constraint); + signals: void valueChanged(); diff --git a/src/tiled/textpropertyedit.h b/src/tiled/textpropertyedit.h index 085a096ae7..4ef2c7360f 100644 --- a/src/tiled/textpropertyedit.h +++ b/src/tiled/textpropertyedit.h @@ -38,6 +38,7 @@ QString escapeNewlines(const QString &string); class TextPropertyEdit : public QWidget { Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL) public: explicit TextPropertyEdit(QWidget *parent = nullptr); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 38d9629d0e..93ec5f6934 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -22,6 +22,7 @@ #include "colorbutton.h" #include "fileedit.h" +#include "textpropertyedit.h" #include "utils.h" #include "propertyeditorwidgets.h" @@ -47,8 +48,23 @@ QWidget *StringProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &QLineEdit::textEdited, this, &StringProperty::setValue); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &QLineEdit::textEdited, this, &StringProperty::setValue); + + return editor; +} + +QWidget *MultilineStringProperty::createEditor(QWidget *parent) +{ + auto editor = new TextPropertyEdit(parent); + auto syncEditor = [=] { + const QSignalBlocker blocker(editor); + editor->setText(value()); + }; + syncEditor(); + + connect(this, &StringProperty::valueChanged, editor, syncEditor); + connect(editor, &TextPropertyEdit::textChanged, this, &StringProperty::setValue); return editor; } @@ -63,8 +79,8 @@ QWidget *UrlProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &FileEdit::fileUrlChanged, this, &UrlProperty::setValue); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &FileEdit::fileUrlChanged, this, &UrlProperty::setValue); return editor; } @@ -72,15 +88,19 @@ QWidget *UrlProperty::createEditor(QWidget *parent) QWidget *IntProperty::createEditor(QWidget *parent) { auto editor = new SpinBox(parent); + editor->setRange(m_minimum, m_maximum); + editor->setSingleStep(m_singleStep); + editor->setSuffix(m_suffix); + auto syncEditor = [=] { const QSignalBlocker blocker(editor); editor->setValue(value()); }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, qOverload(&SpinBox::valueChanged), - this, &IntProperty::setValue); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, qOverload(&SpinBox::valueChanged), + this, &IntProperty::setValue); return editor; } @@ -88,6 +108,8 @@ QWidget *IntProperty::createEditor(QWidget *parent) QWidget *FloatProperty::createEditor(QWidget *parent) { auto editor = new DoubleSpinBox(parent); + editor->setRange(m_minimum, m_maximum); + editor->setSingleStep(m_singleStep); editor->setSuffix(m_suffix); auto syncEditor = [=] { @@ -96,9 +118,9 @@ QWidget *FloatProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, qOverload(&DoubleSpinBox::valueChanged), - this, &FloatProperty::setValue); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, qOverload(&DoubleSpinBox::valueChanged), + this, &FloatProperty::setValue); return editor; } @@ -114,8 +136,8 @@ QWidget *BoolProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &QCheckBox::toggled, this, [=](bool checked) { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &QCheckBox::toggled, this, [=](bool checked) { editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); setValue(checked); }); @@ -132,9 +154,8 @@ QWidget *PointProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &PointEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &PointEdit::valueChanged, this, [this, editor] { setValue(editor->value()); }); @@ -144,15 +165,16 @@ QWidget *PointProperty::createEditor(QWidget *parent) QWidget *PointFProperty::createEditor(QWidget *parent) { auto editor = new PointFEdit(parent); + editor->setSingleStep(m_singleStep); + auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); editor->setValue(value()); }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &PointFEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &PointFEdit::valueChanged, this, [this, editor] { this->setVariantValue(editor->value()); }); @@ -162,15 +184,16 @@ QWidget *PointFProperty::createEditor(QWidget *parent) QWidget *SizeProperty::createEditor(QWidget *parent) { auto editor = new SizeEdit(parent); + editor->setMinimum(m_minimum); + auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); editor->setValue(value()); }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &SizeEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &SizeEdit::valueChanged, this, [this, editor] { setValue(editor->value()); }); @@ -186,9 +209,8 @@ QWidget *SizeFProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &SizeFEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &SizeFEdit::valueChanged, this, [this, editor] { setValue(editor->value()); }); @@ -198,21 +220,32 @@ QWidget *SizeFProperty::createEditor(QWidget *parent) QWidget *RectProperty::createEditor(QWidget *parent) { auto editor = new RectEdit(parent); + editor->setConstraint(m_constraint); + auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); editor->setValue(value()); }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &RectEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &RectEdit::valueChanged, this, [this, editor] { setValue(editor->value()); }); + connect(this, &RectProperty::constraintChanged, + editor, &RectEdit::setConstraint); return editor; } +void RectProperty::setConstraint(const QRect &constraint) +{ + if (m_constraint != constraint) { + m_constraint = constraint; + emit constraintChanged(m_constraint); + } +} + QWidget *RectFProperty::createEditor(QWidget *parent) { auto editor = new RectFEdit(parent); @@ -222,9 +255,8 @@ QWidget *RectFProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &RectFEdit::valueChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &RectFEdit::valueChanged, this, [this, editor] { setValue(editor->value()); }); @@ -241,9 +273,8 @@ QWidget *ColorProperty::createEditor(QWidget *parent) }; syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, &ColorButton::colorChanged, this, - [this, editor] { + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &ColorButton::colorChanged, this, [this, editor] { setValue(editor->color()); }); @@ -305,14 +336,14 @@ QWidget *FontProperty::createEditor(QWidget *parent) syncEditor(); - QObject::connect(this, &Property::valueChanged, fontComboBox, syncEditor); - QObject::connect(fontComboBox, &QFontComboBox::currentFontChanged, this, syncProperty); - QObject::connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), this, syncProperty); - QObject::connect(boldCheckBox, &QCheckBox::toggled, this, syncProperty); - QObject::connect(italicCheckBox, &QCheckBox::toggled, this, syncProperty); - QObject::connect(underlineCheckBox, &QCheckBox::toggled, this, syncProperty); - QObject::connect(strikeoutCheckBox, &QCheckBox::toggled, this, syncProperty); - QObject::connect(kerningCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(this, &Property::valueChanged, fontComboBox, syncEditor); + connect(fontComboBox, &QFontComboBox::currentFontChanged, this, syncProperty); + connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), this, syncProperty); + connect(boldCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(italicCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(underlineCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(strikeoutCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(kerningCheckBox, &QCheckBox::toggled, this, syncProperty); return editor; } @@ -360,9 +391,9 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) syncEditor(); - QObject::connect(this, &Property::valueChanged, editor, syncEditor); - QObject::connect(horizontalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); - QObject::connect(verticalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(horizontalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); + connect(verticalComboBox, qOverload(&QComboBox::currentIndexChanged), this, syncProperty); return editor; } diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 8fdb125ea2..6e59c679a8 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -128,6 +128,12 @@ struct StringProperty : PropertyTemplate QWidget *createEditor(QWidget *parent) override; }; +struct MultilineStringProperty : PropertyTemplate +{ + using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; +}; + struct UrlProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; @@ -141,14 +147,43 @@ struct IntProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setMinimum(int minimum) { m_minimum = minimum; } + void setMaximum(int maximum) { m_maximum = maximum; } + void setSingleStep(int singleStep) { m_singleStep = singleStep; } + void setSuffix(const QString &suffix) { m_suffix = suffix; } + void setRange(int minimum, int maximum) + { + setMinimum(minimum); + setMaximum(maximum); + } + +private: + int m_minimum = std::numeric_limits::min(); + int m_maximum = std::numeric_limits::max(); + int m_singleStep = 1; + QString m_suffix; }; struct FloatProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setMinimum(double minimum) { m_minimum = minimum; } + void setMaximum(double maximum) { m_maximum = maximum; } + void setSingleStep(double singleStep) { m_singleStep = singleStep; } void setSuffix(const QString &suffix) { m_suffix = suffix; } + void setRange(double minimum, double maximum) + { + setMinimum(minimum); + setMaximum(maximum); + } + private: + double m_minimum = -std::numeric_limits::max(); + double m_maximum = std::numeric_limits::max(); + double m_singleStep = 1.0; QString m_suffix; }; @@ -168,12 +203,22 @@ struct PointFProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setSingleStep(double singleStep) { m_singleStep = singleStep; } + +private: + double m_singleStep = 1.0; }; struct SizeProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setMinimum(int minimum) { m_minimum = minimum; } + +private: + int m_minimum; }; struct SizeFProperty : PropertyTemplate @@ -184,8 +229,19 @@ struct SizeFProperty : PropertyTemplate struct RectProperty : PropertyTemplate { + Q_OBJECT + +public: using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setConstraint(const QRect &constraint); + +signals: + void constraintChanged(const QRect &constraint); + +private: + QRect m_constraint; }; struct RectFProperty : PropertyTemplate From c52c920d95568625cee0c917e071549042580a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 9 Sep 2024 19:07:09 +0200 Subject: [PATCH 18/78] Use tool buttons for text styling in FontProperty Makes font property widget take up less vertical space. --- AUTHORS | 6 ++ .../images/scalable/text-bold-symbolic.svg | 2 + .../images/scalable/text-italic-symbolic.svg | 2 + .../scalable/text-strikethrough-symbolic.svg | 2 + .../scalable/text-underline-symbolic.svg | 2 + src/tiled/varianteditor.cpp | 88 ++++++++++++------- 6 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 src/tiled/resources/images/scalable/text-bold-symbolic.svg create mode 100644 src/tiled/resources/images/scalable/text-italic-symbolic.svg create mode 100644 src/tiled/resources/images/scalable/text-strikethrough-symbolic.svg create mode 100644 src/tiled/resources/images/scalable/text-underline-symbolic.svg diff --git a/AUTHORS b/AUTHORS index 9d7b5369b6..6ff7cbddb7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -362,6 +362,12 @@ Icons from the Elementary icon theme (GPLv3) * src/tiled/images/32/dialog-error.png * src/tiled/images/32/dialog-warning.png +Icons from the GNOME project (CC0 1.0 Universal) +* src/tiled/resources/images/scalable/text-bold-symbolic.svg +* src/tiled/resources/images/scalable/text-italic-symbolic.svg +* src/tiled/resources/images/scalable/text-underline-symbolic.svg +* src/tiled/resources/images/scalable/text-strikethrough-symbolic.svg + Tilesets: diff --git a/src/tiled/resources/images/scalable/text-bold-symbolic.svg b/src/tiled/resources/images/scalable/text-bold-symbolic.svg new file mode 100644 index 0000000000..bdbddea8d2 --- /dev/null +++ b/src/tiled/resources/images/scalable/text-bold-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/tiled/resources/images/scalable/text-italic-symbolic.svg b/src/tiled/resources/images/scalable/text-italic-symbolic.svg new file mode 100644 index 0000000000..9fa6e67de6 --- /dev/null +++ b/src/tiled/resources/images/scalable/text-italic-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/tiled/resources/images/scalable/text-strikethrough-symbolic.svg b/src/tiled/resources/images/scalable/text-strikethrough-symbolic.svg new file mode 100644 index 0000000000..6df152de47 --- /dev/null +++ b/src/tiled/resources/images/scalable/text-strikethrough-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/tiled/resources/images/scalable/text-underline-symbolic.svg b/src/tiled/resources/images/scalable/text-underline-symbolic.svg new file mode 100644 index 0000000000..ff520eb3c7 --- /dev/null +++ b/src/tiled/resources/images/scalable/text-underline-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 93ec5f6934..abbd8bd2bb 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -37,6 +37,7 @@ #include #include #include +#include namespace Tiled { @@ -284,53 +285,78 @@ QWidget *ColorProperty::createEditor(QWidget *parent) QWidget *FontProperty::createEditor(QWidget *parent) { auto editor = new QWidget(parent); - auto layout = new QVBoxLayout(editor); auto fontComboBox = new QFontComboBox(editor); + auto sizeSpinBox = new QSpinBox(editor); - auto boldCheckBox = new QCheckBox(tr("Bold"), editor); - auto italicCheckBox = new QCheckBox(tr("Italic"), editor); - auto underlineCheckBox = new QCheckBox(tr("Underline"), editor); - auto strikeoutCheckBox = new QCheckBox(tr("Strikeout"), editor); - auto kerningCheckBox = new QCheckBox(tr("Kerning"), editor); sizeSpinBox->setRange(1, 999); sizeSpinBox->setSuffix(tr(" px")); sizeSpinBox->setKeyboardTracking(false); + + auto bold = new QToolButton(editor); + bold->setIcon(QIcon(QStringLiteral("://images/scalable/text-bold-symbolic.svg"))); + bold->setToolTip(tr("Bold")); + bold->setCheckable(true); + + auto italic = new QToolButton(editor); + italic->setIcon(QIcon(QStringLiteral("://images/scalable/text-italic-symbolic.svg"))); + italic->setToolTip(tr("Italic")); + italic->setCheckable(true); + + auto underline = new QToolButton(editor); + underline->setIcon(QIcon(QStringLiteral("://images/scalable/text-underline-symbolic.svg"))); + underline->setToolTip(tr("Underline")); + underline->setCheckable(true); + + auto strikeout = new QToolButton(editor); + strikeout->setIcon(QIcon(QStringLiteral("://images/scalable/text-strikethrough-symbolic.svg"))); + strikeout->setToolTip(tr("Strikethrough")); + strikeout->setCheckable(true); + + auto kerning = new QCheckBox(tr("Kerning"), editor); + + auto layout = new QVBoxLayout(editor); layout->setContentsMargins(QMargins()); layout->setSpacing(Utils::dpiScaled(3)); layout->addWidget(fontComboBox); layout->addWidget(sizeSpinBox); - layout->addWidget(boldCheckBox); - layout->addWidget(italicCheckBox); - layout->addWidget(underlineCheckBox); - layout->addWidget(strikeoutCheckBox); - layout->addWidget(kerningCheckBox); + + auto buttonsLayout = new QHBoxLayout; + buttonsLayout->setContentsMargins(QMargins()); + buttonsLayout->addWidget(bold); + buttonsLayout->addWidget(italic); + buttonsLayout->addWidget(underline); + buttonsLayout->addWidget(strikeout); + buttonsLayout->addStretch(); + + layout->addLayout(buttonsLayout); + layout->addWidget(kerning); auto syncEditor = [=] { const auto font = value(); const QSignalBlocker fontBlocker(fontComboBox); const QSignalBlocker sizeBlocker(sizeSpinBox); - const QSignalBlocker boldBlocker(boldCheckBox); - const QSignalBlocker italicBlocker(italicCheckBox); - const QSignalBlocker underlineBlocker(underlineCheckBox); - const QSignalBlocker strikeoutBlocker(strikeoutCheckBox); - const QSignalBlocker kerningBlocker(kerningCheckBox); + const QSignalBlocker boldBlocker(bold); + const QSignalBlocker italicBlocker(italic); + const QSignalBlocker underlineBlocker(underline); + const QSignalBlocker strikeoutBlocker(strikeout); + const QSignalBlocker kerningBlocker(kerning); fontComboBox->setCurrentFont(font); sizeSpinBox->setValue(font.pixelSize()); - boldCheckBox->setChecked(font.bold()); - italicCheckBox->setChecked(font.italic()); - underlineCheckBox->setChecked(font.underline()); - strikeoutCheckBox->setChecked(font.strikeOut()); - kerningCheckBox->setChecked(font.kerning()); + bold->setChecked(font.bold()); + italic->setChecked(font.italic()); + underline->setChecked(font.underline()); + strikeout->setChecked(font.strikeOut()); + kerning->setChecked(font.kerning()); }; auto syncProperty = [=] { auto font = fontComboBox->currentFont(); font.setPixelSize(sizeSpinBox->value()); - font.setBold(boldCheckBox->isChecked()); - font.setItalic(italicCheckBox->isChecked()); - font.setUnderline(underlineCheckBox->isChecked()); - font.setStrikeOut(strikeoutCheckBox->isChecked()); - font.setKerning(kerningCheckBox->isChecked()); + font.setBold(bold->isChecked()); + font.setItalic(italic->isChecked()); + font.setUnderline(underline->isChecked()); + font.setStrikeOut(strikeout->isChecked()); + font.setKerning(kerning->isChecked()); setValue(font); }; @@ -339,11 +365,11 @@ QWidget *FontProperty::createEditor(QWidget *parent) connect(this, &Property::valueChanged, fontComboBox, syncEditor); connect(fontComboBox, &QFontComboBox::currentFontChanged, this, syncProperty); connect(sizeSpinBox, qOverload(&QSpinBox::valueChanged), this, syncProperty); - connect(boldCheckBox, &QCheckBox::toggled, this, syncProperty); - connect(italicCheckBox, &QCheckBox::toggled, this, syncProperty); - connect(underlineCheckBox, &QCheckBox::toggled, this, syncProperty); - connect(strikeoutCheckBox, &QCheckBox::toggled, this, syncProperty); - connect(kerningCheckBox, &QCheckBox::toggled, this, syncProperty); + connect(bold, &QAbstractButton::toggled, this, syncProperty); + connect(italic, &QAbstractButton::toggled, this, syncProperty); + connect(underline, &QAbstractButton::toggled, this, syncProperty); + connect(strikeout, &QAbstractButton::toggled, this, syncProperty); + connect(kerning, &QAbstractButton::toggled, this, syncProperty); return editor; } From 2cb3d25429e376b1ca80f1210a0add27a1517e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 12 Sep 2024 18:29:35 +0200 Subject: [PATCH 19/78] Progress on various property editor features * Added support for editing top-level custom properties (excluding support for classes and enums for now). * Added support for collapsing the property groups, based on a new GroupProperty which creates nested VariantEditor instances. * Removed QVariant based getter/setter from Property, since it doesn't appear to be useful. * Added Property::DisplayMode, which is used to tell apart group properties and separators from regular properties. * VariantEditor is no longer a scroll area itself, to enable nesting. --- src/tiled/fileedit.cpp | 2 +- src/tiled/propertieswidget.cpp | 313 +++++++++++++++++++--------- src/tiled/propertieswidget.h | 3 + src/tiled/propertyeditorwidgets.cpp | 72 +++++++ src/tiled/propertyeditorwidgets.h | 43 ++++ src/tiled/textpropertyedit.cpp | 2 +- src/tiled/varianteditor.cpp | 117 +++++++---- src/tiled/varianteditor.h | 80 ++++--- 8 files changed, 459 insertions(+), 173 deletions(-) diff --git a/src/tiled/fileedit.cpp b/src/tiled/fileedit.cpp index e6804e999b..5e1b09e266 100644 --- a/src/tiled/fileedit.cpp +++ b/src/tiled/fileedit.cpp @@ -45,7 +45,7 @@ FileEdit::FileEdit(QWidget *parent) mOkTextColor = mLineEdit->palette().color(QPalette::Active, QPalette::Text); QToolButton *button = new QToolButton(this); - button->setText(QLatin1String("...")); + button->setText(QStringLiteral("…")); button->setAutoRaise(true); button->setToolTip(tr("Choose")); layout->addWidget(mLineEdit); diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 0bdb287d6c..3f40d76ffe 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -36,6 +36,7 @@ #include "compression.h" #include "mapdocument.h" #include "objectgroup.h" +#include "objectrefedit.h" #include "objecttemplate.h" #include "preferences.h" #include "propertybrowser.h" @@ -43,6 +44,7 @@ #include "tilesetdocument.h" #include "utils.h" #include "varianteditor.h" +#include "variantpropertymanager.h" #include "wangoverlay.h" #include @@ -193,10 +195,47 @@ template<> EnumData enumData() } +class ObjectRefProperty : public PropertyTemplate +{ + Q_OBJECT + +public: + using PropertyTemplate::PropertyTemplate; + + QWidget *createEditor(QWidget *parent) override + { + auto editor = new ObjectRefEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(value()); + }; + syncEditor(); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &ObjectRefEdit::valueChanged, + this, [this](const DisplayObjectRef &value) { + setValue(value); + }); + return editor; + } +}; + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} - , mPropertyBrowser(new VariantEditor(this)) + , mScrollArea(new QScrollArea(this)) { + auto scrollWidget = new QWidget(mScrollArea); + scrollWidget->setBackgroundRole(QPalette::AlternateBase); + + auto verticalLayout = new QVBoxLayout(scrollWidget); + mPropertyBrowser = new VariantEditor(scrollWidget); + verticalLayout->addWidget(mPropertyBrowser); + verticalLayout->addStretch(); + verticalLayout->setContentsMargins(QMargins()); + + mScrollArea->setWidget(scrollWidget); + mScrollArea->setWidgetResizable(true); + mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); @@ -231,7 +270,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - layout->addWidget(mPropertyBrowser); + layout->addWidget(mScrollArea); layout->addWidget(toolBar); setLayout(layout); @@ -569,6 +608,26 @@ class MapProperties : public ObjectProperties push(new ChangeMapProperty(mapDocument(), value)); }); + mMapProperties = new GroupProperty(tr("Map")); + mMapProperties->addProperty(mClassProperty); + mMapProperties->addSeparator(); + mMapProperties->addProperty(mOrientationProperty); + mMapProperties->addProperty(mSizeProperty); + mMapProperties->addProperty(mInfiniteProperty); + mMapProperties->addProperty(mTileSizeProperty); + mMapProperties->addProperty(mHexSideLengthProperty); + mMapProperties->addProperty(mStaggerAxisProperty); + mMapProperties->addProperty(mStaggerIndexProperty); + mMapProperties->addSeparator(); + mMapProperties->addProperty(mParallaxOriginProperty); + mMapProperties->addSeparator(); + mMapProperties->addProperty(mLayerDataFormatProperty); + mMapProperties->addProperty(mChunkSizeProperty); + mMapProperties->addProperty(mCompressionLevelProperty); + mMapProperties->addSeparator(); + mMapProperties->addProperty(mRenderOrderProperty); + mMapProperties->addProperty(mBackgroundColorProperty); + updateEnabledState(); connect(document, &Document::changed, this, &MapProperties::onChanged); @@ -576,25 +635,7 @@ class MapProperties : public ObjectProperties void populateEditor(VariantEditor *editor) override { - editor->addHeader(tr("Map")); - editor->addProperty(mClassProperty); - editor->addSeparator(); - editor->addProperty(mOrientationProperty); - editor->addProperty(mSizeProperty); - editor->addProperty(mInfiniteProperty); - editor->addProperty(mTileSizeProperty); - editor->addProperty(mHexSideLengthProperty); - editor->addProperty(mStaggerAxisProperty); - editor->addProperty(mStaggerIndexProperty); - editor->addSeparator(); - editor->addProperty(mParallaxOriginProperty); - editor->addSeparator(); - editor->addProperty(mLayerDataFormatProperty); - editor->addProperty(mChunkSizeProperty); - editor->addProperty(mCompressionLevelProperty); - editor->addSeparator(); - editor->addProperty(mRenderOrderProperty); - editor->addProperty(mBackgroundColorProperty); + editor->addProperty(mMapProperties); } private: @@ -682,6 +723,7 @@ class MapProperties : public ObjectProperties return mapDocument()->map(); } + GroupProperty *mMapProperties; Property *mOrientationProperty; Property *mSizeProperty; SizeProperty *mTileSizeProperty; @@ -767,23 +809,25 @@ class LayerProperties : public ObjectProperties }); mParallaxFactorProperty->setSingleStep(0.1); + mLayerProperties = new GroupProperty(tr("Layer")); + mLayerProperties->addProperty(mIdProperty); + mLayerProperties->addProperty(mNameProperty); + mLayerProperties->addProperty(mClassProperty); + mLayerProperties->addSeparator(); + mLayerProperties->addProperty(mVisibleProperty); + mLayerProperties->addProperty(mLockedProperty); + mLayerProperties->addProperty(mOpacityProperty); + mLayerProperties->addProperty(mTintColorProperty); + mLayerProperties->addProperty(mOffsetProperty); + mLayerProperties->addProperty(mParallaxFactorProperty); + connect(document, &Document::changed, this, &LayerProperties::onChanged); } void populateEditor(VariantEditor *editor) override { - editor->addHeader(tr("Layer")); - editor->addProperty(mIdProperty); - editor->addProperty(mNameProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); - editor->addProperty(mVisibleProperty); - editor->addProperty(mLockedProperty); - editor->addProperty(mOpacityProperty); - editor->addProperty(mTintColorProperty); - editor->addProperty(mOffsetProperty); - editor->addProperty(mParallaxFactorProperty); + editor->addProperty(mLayerProperties); } protected: @@ -820,6 +864,7 @@ class LayerProperties : public ObjectProperties return static_cast(mObject); } + GroupProperty *mLayerProperties; Property *mIdProperty; Property *mNameProperty; Property *mVisibleProperty; @@ -867,17 +912,19 @@ class ImageLayerProperties : public LayerProperties [this](const bool &value) { push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, value)); }); + + mImageLayerProperties = new GroupProperty(tr("Image Layer")); + mImageLayerProperties->addProperty(mImageProperty); + mImageLayerProperties->addProperty(mTransparentColorProperty); + mImageLayerProperties->addSeparator(); + mImageLayerProperties->addProperty(mRepeatXProperty); + mImageLayerProperties->addProperty(mRepeatYProperty); } void populateEditor(VariantEditor *editor) override { LayerProperties::populateEditor(editor); - editor->addHeader(tr("Image Layer")); - editor->addProperty(mImageProperty); - editor->addProperty(mTransparentColorProperty); - editor->addSeparator(); - editor->addProperty(mRepeatXProperty); - editor->addProperty(mRepeatYProperty); + editor->addProperty(mImageLayerProperties); } private: @@ -907,6 +954,7 @@ class ImageLayerProperties : public LayerProperties return static_cast(mObject); } + GroupProperty *mImageLayerProperties; UrlProperty *mImageProperty; Property *mTransparentColorProperty; Property *mRepeatXProperty; @@ -1083,6 +1131,21 @@ class TilesetProperties : public ObjectProperties // push(new ChangeTilesetImage(tilesetDocument(), value.toString())); }); + mTilesetProperties = new GroupProperty(tr("Tileset")); + mTilesetProperties->addProperty(mNameProperty); + mTilesetProperties->addProperty(mClassProperty); + mTilesetProperties->addSeparator(); + mTilesetProperties->addProperty(mObjectAlignmentProperty); + mTilesetProperties->addProperty(mTileOffsetProperty); + mTilesetProperties->addProperty(mTileRenderSizeProperty); + mTilesetProperties->addProperty(mFillModeProperty); + mTilesetProperties->addProperty(mBackgroundColorProperty); + mTilesetProperties->addProperty(mOrientationProperty); + mTilesetProperties->addProperty(mGridSizeProperty); + mTilesetProperties->addProperty(mColumnCountProperty); + mTilesetProperties->addProperty(mAllowedTransformationsProperty); + mTilesetProperties->addProperty(mImageProperty); + updateEnabledState(); connect(tilesetDocument(), &Document::changed, this, &TilesetProperties::onChanged); @@ -1099,20 +1162,7 @@ class TilesetProperties : public ObjectProperties void populateEditor(VariantEditor *editor) { - editor->addHeader(tr("Tileset")); - editor->addProperty(mNameProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); - editor->addProperty(mObjectAlignmentProperty); - editor->addProperty(mTileOffsetProperty); - editor->addProperty(mTileRenderSizeProperty); - editor->addProperty(mFillModeProperty); - editor->addProperty(mBackgroundColorProperty); - editor->addProperty(mOrientationProperty); - editor->addProperty(mGridSizeProperty); - editor->addProperty(mColumnCountProperty); - editor->addProperty(mAllowedTransformationsProperty); - editor->addProperty(mImageProperty); + editor->addProperty(mTilesetProperties); } private: @@ -1160,6 +1210,7 @@ class TilesetProperties : public ObjectProperties return tilesetDocument()->tileset().data(); } + GroupProperty *mTilesetProperties; Property *mNameProperty; Property *mObjectAlignmentProperty; Property *mTileOffsetProperty; @@ -1312,45 +1363,47 @@ class MapObjectProperties : public ObjectProperties changeMapObject(MapObject::TextColorProperty, value); }); - connect(document, &Document::changed, - this, &MapObjectProperties::onChanged); - - updateEnabledState(); - } - - void populateEditor(VariantEditor *editor) override - { - editor->addHeader(tr("Object")); - editor->addProperty(mIdProperty); - editor->addProperty(mTemplateProperty); - editor->addProperty(mNameProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); + mObjectProperties = new GroupProperty(tr("Object")); + mObjectProperties->addProperty(mIdProperty); + mObjectProperties->addProperty(mTemplateProperty); + mObjectProperties->addProperty(mNameProperty); + mObjectProperties->addProperty(mClassProperty); + mObjectProperties->addSeparator(); if (mapDocument()->allowHidingObjects()) - editor->addProperty(mVisibleProperty); + mObjectProperties->addProperty(mVisibleProperty); - editor->addProperty(mPositionProperty); + mObjectProperties->addProperty(mPositionProperty); if (mapObject()->hasDimensions()) - editor->addProperty(mSizeProperty); + mObjectProperties->addProperty(mSizeProperty); if (mapObject()->canRotate()) - editor->addProperty(mRotationProperty); + mObjectProperties->addProperty(mRotationProperty); if (mapObject()->isTileObject()) { - editor->addSeparator(); - editor->addProperty(mFlippingProperty); + mObjectProperties->addSeparator(); + mObjectProperties->addProperty(mFlippingProperty); } if (mapObject()->shape() == MapObject::Text) { - editor->addSeparator(); - editor->addProperty(mTextProperty); - editor->addProperty(mTextAlignmentProperty); - editor->addProperty(mTextFontProperty); - editor->addProperty(mTextWordWrapProperty); - editor->addProperty(mTextColorProperty); + mObjectProperties->addSeparator(); + mObjectProperties->addProperty(mTextProperty); + mObjectProperties->addProperty(mTextAlignmentProperty); + mObjectProperties->addProperty(mTextFontProperty); + mObjectProperties->addProperty(mTextWordWrapProperty); + mObjectProperties->addProperty(mTextColorProperty); } + + connect(document, &Document::changed, + this, &MapObjectProperties::onChanged); + + updateEnabledState(); + } + + void populateEditor(VariantEditor *editor) override + { + editor->addProperty(mObjectProperties); } private: @@ -1417,6 +1470,7 @@ class MapObjectProperties : public ObjectProperties push(new ChangeMapObject(mapDocument(), mapObject(), property, value)); } + GroupProperty *mObjectProperties; Property *mIdProperty; Property *mTemplateProperty; Property *mNameProperty; @@ -1480,6 +1534,17 @@ class TileProperties : public ObjectProperties mProbabilityProperty->setToolTip(tr("Relative chance this tile will be picked")); mProbabilityProperty->setMinimum(0.0); + mTileProperties = new GroupProperty(tr("Tile")); + mTileProperties->addProperty(mIdProperty); + mTileProperties->addProperty(mClassProperty); + mTileProperties->addSeparator(); + + if (!tile()->imageSource().isEmpty()) + mTileProperties->addProperty(mImageProperty); + + mTileProperties->addProperty(mRectangleProperty); + mTileProperties->addProperty(mProbabilityProperty); + // annoying... maybe we should somehow always have the relevant TilesetDocument if (auto tilesetDocument = qobject_cast(document)) { connect(tilesetDocument, &TilesetDocument::tileImageSourceChanged, @@ -1500,16 +1565,7 @@ class TileProperties : public ObjectProperties void populateEditor(VariantEditor *editor) override { - editor->addHeader(tr("Tile")); - editor->addProperty(mIdProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); - - if (!tile()->imageSource().isEmpty()) - editor->addProperty(mImageProperty); - - editor->addProperty(mRectangleProperty); - editor->addProperty(mProbabilityProperty); + editor->addProperty(mTileProperties); } private: @@ -1549,6 +1605,7 @@ class TileProperties : public ObjectProperties return static_cast(mObject); } + GroupProperty *mTileProperties; Property *mIdProperty; UrlProperty *mImageProperty; RectProperty *mRectangleProperty; @@ -1588,6 +1645,13 @@ class WangSetProperties : public ObjectProperties }); mColorCountProperty->setRange(0, WangId::MAX_COLOR_COUNT); + mWangSetProperties = new GroupProperty(tr("Terrain Set")); + mWangSetProperties->addProperty(mNameProperty); + mWangSetProperties->addProperty(mClassProperty); + mWangSetProperties->addSeparator(); + mWangSetProperties->addProperty(mTypeProperty); + mWangSetProperties->addProperty(mColorCountProperty); + connect(document, &Document::changed, this, &WangSetProperties::onChanged); @@ -1596,12 +1660,7 @@ class WangSetProperties : public ObjectProperties void populateEditor(VariantEditor *editor) override { - editor->addHeader(tr("Terrain Set")); - editor->addProperty(mNameProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); - editor->addProperty(mTypeProperty); - editor->addProperty(mColorCountProperty); + editor->addProperty(mWangSetProperties); } private: @@ -1647,6 +1706,7 @@ class WangSetProperties : public ObjectProperties return static_cast(mObject); } + GroupProperty *mWangSetProperties; Property *mNameProperty; Property *mTypeProperty; IntProperty *mColorCountProperty; @@ -1683,6 +1743,13 @@ class WangColorProperties : public ObjectProperties }); mProbabilityProperty->setMinimum(0.01); + mWangColorProperties = new GroupProperty(tr("Terrain")); + mWangColorProperties->addProperty(mNameProperty); + mWangColorProperties->addProperty(mClassProperty); + mWangColorProperties->addSeparator(); + mWangColorProperties->addProperty(mColorProperty); + mWangColorProperties->addProperty(mProbabilityProperty); + connect(document, &Document::changed, this, &WangColorProperties::onChanged); @@ -1691,12 +1758,7 @@ class WangColorProperties : public ObjectProperties void populateEditor(VariantEditor *editor) override { - editor->addHeader(tr("Terrain")); - editor->addProperty(mNameProperty); - editor->addProperty(mClassProperty); - editor->addSeparator(); - editor->addProperty(mColorProperty); - editor->addProperty(mProbabilityProperty); + editor->addProperty(mWangColorProperties); } private: @@ -1743,6 +1805,7 @@ class WangColorProperties : public ObjectProperties return static_cast(mObject); } + GroupProperty *mWangColorProperties; Property *mNameProperty; Property *mColorProperty; FloatProperty *mProbabilityProperty; @@ -1818,6 +1881,56 @@ void PropertiesWidget::currentObjectChanged(Object *object) if (mPropertiesObject) mPropertiesObject->populateEditor(mPropertyBrowser); + GroupProperty *customProperties = new GroupProperty(tr("Custom Properties")); + + QMapIterator it(object ? object->properties() : Properties()); + PropertyFactory factory; + + while (it.hasNext()) { + it.next(); + + const auto &name = it.key(); + const auto &value = it.value(); + + Property *property = nullptr; + + switch (value.userType()) { + case QMetaType::Bool: + case QMetaType::QColor: + case QMetaType::Double: + case QMetaType::Int: + case QMetaType::QString: { + auto get = [object, name] { return object->property(name); }; + auto set = [this, object, name] (const QVariant &value) { + mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, value)); + }; + property = factory.createProperty(name, std::move(get), std::move(set)); + break; + } + default: + if (value.userType() == filePathTypeId()) { + auto get = [object, name] { return object->property(name).value().url; }; + auto set = [this, object, name](const QUrl &value) { + mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(FilePath { value }))); + }; + property = new UrlProperty(name, get, set); + } else if (value.userType() == objectRefTypeId()) { + auto get = [this, object, name] { return DisplayObjectRef(object->property(name).value(), static_cast(mDocument)); }; + auto set = [this, object, name](const DisplayObjectRef &value) { + mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(value.ref))); + }; + property = new ObjectRefProperty(name, get, set); + } + // todo: PropertyValue (enum and class values) + break; + } + + if (property) + customProperties->addProperty(property); + } + + mPropertyBrowser->addProperty(customProperties); + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; bool isTileset = object && object->isPartOfTileset(); bool enabled = object && (!isTileset || editingTileset); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 443529e38a..bda0cf672e 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -22,6 +22,8 @@ #include +class QScrollArea; + namespace Tiled { class Object; @@ -76,6 +78,7 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; + QScrollArea *mScrollArea; VariantEditor *mPropertyBrowser; QAction *mActionAddProperty; QAction *mActionRemoveProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 706a91ab48..e779971053 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -1,3 +1,23 @@ +/* + * propertyeditorwidgets.cpp + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + #include "propertyeditorwidgets.h" #include "utils.h" @@ -7,6 +27,7 @@ #include #include #include +#include namespace Tiled { @@ -458,6 +479,57 @@ void ElidingLabel::paintEvent(QPaintEvent *) } +HeaderWidget::HeaderWidget(const QString &text, QWidget *parent) + : ElidingLabel(text, parent) +{ + setBackgroundRole(QPalette::Dark); + setForegroundRole(QPalette::BrightText); + setAutoFillBackground(true); + + const int verticalMargin = Utils::dpiScaled(3); + const int horizontalMargin = Utils::dpiScaled(6); + const int branchIndicatorWidth = Utils::dpiScaled(14); + setContentsMargins(horizontalMargin + branchIndicatorWidth, + verticalMargin, horizontalMargin, verticalMargin); +} + +void HeaderWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + m_checked = !m_checked; + emit toggled(m_checked); + } + + ElidingLabel::mousePressEvent(event); +} + +void HeaderWidget::paintEvent(QPaintEvent *) +{ + const QRect cr = contentsRect(); + const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; + const int align = QStyle::visualAlignment(dir, {}); + const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight + : Qt::TextForceRightToLeft); + + QStyleOption branchOption; + branchOption.initFrom(this); + branchOption.rect = QRect(0, 0, contentsMargins().left(), height()); + branchOption.state = QStyle::State_Children; + if (m_checked) + branchOption.state |= QStyle::State_Open; + + QStylePainter p(this); + p.drawPrimitive(QStyle::PE_IndicatorBranch, branchOption); + + QStyleOption opt; + opt.initFrom(this); + + const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); + + p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); +} + + QSize LineEditLabel::sizeHint() const { auto hint = ElidingLabel::sizeHint(); diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 84062ad323..9f38b945c6 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -1,3 +1,23 @@ +/* + * propertyeditorwidgets.h + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + #pragma once #include @@ -242,12 +262,35 @@ class ElidingLabel : public QLabel ElidingLabel(const QString &text, QWidget *parent = nullptr); QSize minimumSizeHint() const override; + +protected: void paintEvent(QPaintEvent *) override; private: bool m_isElided = false; }; +/** + * A header widget that can be toggled. + */ +class HeaderWidget : public ElidingLabel +{ + Q_OBJECT + +public: + HeaderWidget(const QString &text, QWidget *parent = nullptr); + +signals: + void toggled(bool checked); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *) override; + +private: + bool m_checked = true; +}; + /** * A label that matches its preferred height with that of a line edit. */ diff --git a/src/tiled/textpropertyedit.cpp b/src/tiled/textpropertyedit.cpp index 147852cc22..85c131ebab 100644 --- a/src/tiled/textpropertyedit.cpp +++ b/src/tiled/textpropertyedit.cpp @@ -117,7 +117,7 @@ TextPropertyEdit::TextPropertyEdit(QWidget *parent) setFocusProxy(mLineEdit); QToolButton *button = new QToolButton(this); - button->setText(QStringLiteral("...")); + button->setText(QStringLiteral("…")); button->setAutoRaise(true); // Set a validator that replaces newline characters by literal "\\n". diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index abbd8bd2bb..9c8362182d 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -41,6 +41,30 @@ namespace Tiled { +void Property::setToolTip(const QString &toolTip) +{ + if (m_toolTip != toolTip) { + m_toolTip = toolTip; + emit toolTipChanged(toolTip); + } +} + +void Property::setEnabled(bool enabled) +{ + if (m_enabled != enabled) { + m_enabled = enabled; + emit enabledChanged(enabled); + } +} + +QWidget *GroupProperty::createEditor(QWidget *parent) +{ + auto widget = new VariantEditor(parent); + for (auto property : std::as_const(m_subProperties)) + widget->addProperty(property); + return widget; +} + QWidget *StringProperty::createEditor(QWidget *parent) { auto editor = new QLineEdit(parent); @@ -176,7 +200,7 @@ QWidget *PointFProperty::createEditor(QWidget *parent) connect(this, &Property::valueChanged, editor, syncEditor); connect(editor, &PointFEdit::valueChanged, this, [this, editor] { - this->setVariantValue(editor->value()); + this->setValue(editor->value()); }); return editor; @@ -426,20 +450,11 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) VariantEditor::VariantEditor(QWidget *parent) - : QScrollArea(parent) + : QWidget(parent) { - m_widget = new QWidget; - m_widget->setBackgroundRole(QPalette::AlternateBase); - auto verticalLayout = new QVBoxLayout(m_widget); - m_gridLayout = new QGridLayout; - verticalLayout->addLayout(m_gridLayout); - verticalLayout->addStretch(); - verticalLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(6)); - - setWidget(m_widget); - setWidgetResizable(true); - - m_gridLayout->setContentsMargins(QMargins()); + m_gridLayout = new QGridLayout(this); + + m_gridLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(3)); m_gridLayout->setSpacing(Utils::dpiScaled(3)); m_gridLayout->setColumnStretch(LabelColumn, 2); @@ -477,24 +492,20 @@ void VariantEditor::clear() m_rowIndex = 0; } -void VariantEditor::addHeader(const QString &text) +HeaderWidget *VariantEditor::addHeader(const QString &text) { - auto label = new ElidingLabel(text, m_widget); - label->setBackgroundRole(QPalette::Dark); - const int verticalMargin = Utils::dpiScaled(3); - const int horizontalMargin = Utils::dpiScaled(6); - label->setContentsMargins(horizontalMargin, verticalMargin, - horizontalMargin, verticalMargin); + auto headerWidget = new HeaderWidget(text, this); - label->setAutoFillBackground(true); + m_gridLayout->addWidget(headerWidget, m_rowIndex, 0, 1, ColumnCount); - m_gridLayout->addWidget(label, m_rowIndex, 0, 1, ColumnCount); ++m_rowIndex; + + return headerWidget; } void VariantEditor::addSeparator() { - auto separator = new QFrame(m_widget); + auto separator = new QFrame(this); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); @@ -504,23 +515,49 @@ void VariantEditor::addSeparator() void VariantEditor::addProperty(Property *property) { - auto label = new LineEditLabel(property->name(), m_widget); - label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - label->setToolTip(property->toolTip()); - label->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, label, &QLabel::setEnabled); - m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); - - if (auto editor = createEditor(property)) { - editor->setToolTip(property->toolTip()); - editor->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); - m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); + switch (property->displayMode()) { + case Property::DisplayMode::Default: { + auto label = new LineEditLabel(property->name(), this); + label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + label->setToolTip(property->toolTip()); + label->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, label, &QLabel::setEnabled); + m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); + + if (auto editor = createEditor(property)) { + editor->setToolTip(property->toolTip()); + editor->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); + m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); + } + + ++m_rowIndex; + break; } + case Property::DisplayMode::Header: { + auto headerWidget = addHeader(property->name()); - ++m_rowIndex; + if (auto editor = createEditor(property)) { + connect(headerWidget, &HeaderWidget::toggled, + editor, [this, editor](bool checked) { + editor->setVisible(checked); + + // needed to avoid flickering when hiding the editor + layout()->activate(); + }); + + m_gridLayout->addWidget(editor, m_rowIndex, 0, 1, ColumnCount); + ++m_rowIndex; + } + + break; + } + case Property::DisplayMode::Separator: + addSeparator(); + break; + } } #if 0 @@ -554,7 +591,7 @@ void VariantEditor::addValue(const QVariant &value) QWidget *VariantEditor::createEditor(Property *property) { - if (const auto editor = property->createEditor(m_widget)) { + if (const auto editor = property->createEditor(this)) { editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); return editor; } else { diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 6e59c679a8..37afb146e3 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -32,6 +32,8 @@ class QGridLayout; namespace Tiled { +class HeaderWidget; + /** * A property represents a named value that can create its own edit widget. */ @@ -40,10 +42,15 @@ class Property : public QObject Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) - Q_PROPERTY(QVariant value READ variantValue WRITE setVariantValue NOTIFY valueChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) public: + enum class DisplayMode { + Default, + Header, + Separator + }; + Property(const QString &name, QObject *parent = nullptr) : QObject(parent) , m_name(name) @@ -52,25 +59,12 @@ class Property : public QObject const QString &name() const { return m_name; } const QString &toolTip() const { return m_toolTip; } - void setToolTip(const QString &toolTip) - { - if (m_toolTip != toolTip) { - m_toolTip = toolTip; - emit toolTipChanged(toolTip); - } - } + void setToolTip(const QString &toolTip); bool isEnabled() const { return m_enabled; } - void setEnabled(bool enabled) - { - if (m_enabled != enabled) { - m_enabled = enabled; - emit enabledChanged(enabled); - } - } + void setEnabled(bool enabled); - virtual QVariant variantValue() const = 0; - virtual void setVariantValue(const QVariant &value) = 0; + virtual DisplayMode displayMode() const { return DisplayMode::Default; } virtual QWidget *createEditor(QWidget *parent) = 0; @@ -85,6 +79,42 @@ class Property : public QObject bool m_enabled = true; }; +class Separator final : public Property +{ + Q_OBJECT + +public: + Separator(QObject *parent = nullptr) + : Property(QString(), parent) + {} + + DisplayMode displayMode() const override { return DisplayMode::Separator; } + + QWidget *createEditor(QWidget */*parent*/) override { return nullptr; } +}; + +class GroupProperty : public Property +{ + Q_OBJECT + +public: + GroupProperty(const QString &name, QObject *parent = nullptr) + : Property(name, parent) + {} + + DisplayMode displayMode() const override { return DisplayMode::Header; } + + QWidget *createEditor(QWidget *parent) override; + + void addProperty(Property *property) { m_subProperties.append(property); } + void addSeparator() { m_subProperties.append(new Separator(this)); } + + const QList &subProperties() const { return m_subProperties; } + +private: + QList m_subProperties; +}; + /** * A helper class for creating a property that wraps a value of a given type. */ @@ -106,17 +136,6 @@ class PropertyTemplate : public Property Type value() const { return m_get(); } void setValue(const Type &value) { m_set(value); } - QVariant variantValue() const override - { - return QVariant::fromValue(m_get()); - } - - void setVariantValue(const QVariant &value) override - { - if (m_set) - m_set(value.value()); - } - private: std::function m_get; std::function m_set; @@ -357,7 +376,7 @@ class EnumProperty : public IntProperty }; -class VariantEditor : public QScrollArea +class VariantEditor : public QWidget { Q_OBJECT @@ -365,7 +384,7 @@ class VariantEditor : public QScrollArea VariantEditor(QWidget *parent = nullptr); void clear(); - void addHeader(const QString &text); + HeaderWidget *addHeader(const QString &text); void addSeparator(); void addProperty(Property *property); // void addValue(const QVariant &value); @@ -382,7 +401,6 @@ class VariantEditor : public QScrollArea ColumnCount, }; - QWidget *m_widget; QGridLayout *m_gridLayout; int m_rowIndex = 0; }; From 09f16a64a9670411b5792f136cfee1f4c3789325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 16 Sep 2024 13:56:11 +0200 Subject: [PATCH 20/78] Made a special case for the image layer repeat setting Also made some other small tweaks. --- src/libtiled/imagelayer.cpp | 3 +- src/libtiled/imagelayer.h | 43 ++++++++-------- src/tiled/propertieswidget.cpp | 79 +++++++++++++++++++++-------- src/tiled/propertyeditorwidgets.cpp | 26 +++------- src/tiled/varianteditor.cpp | 2 +- 5 files changed, 91 insertions(+), 62 deletions(-) diff --git a/src/libtiled/imagelayer.cpp b/src/libtiled/imagelayer.cpp index 9086d87c8a..ab1169115c 100644 --- a/src/libtiled/imagelayer.cpp +++ b/src/libtiled/imagelayer.cpp @@ -104,8 +104,7 @@ ImageLayer *ImageLayer::initializeClone(ImageLayer *clone) const clone->mImageSource = mImageSource; clone->mTransparentColor = mTransparentColor; clone->mImage = mImage; - clone->mRepeatX = mRepeatX; - clone->mRepeatY = mRepeatY; + clone->mRepetition = mRepetition; return clone; } diff --git a/src/libtiled/imagelayer.h b/src/libtiled/imagelayer.h index 2a63df2071..faf00a4c6b 100644 --- a/src/libtiled/imagelayer.h +++ b/src/libtiled/imagelayer.h @@ -46,6 +46,19 @@ namespace Tiled { class TILEDSHARED_EXPORT ImageLayer : public Layer { public: + enum Repetition { + /** + * Makes the image repeat along the X axis. + */ + RepeatX = 0x1, + + /** + * Makes the image repeat along the Y axis. + */ + RepeatY = 0x2, + }; + Q_DECLARE_FLAGS(RepetitionFlags, Repetition) + ImageLayer(const QString &name, int x, int y); ~ImageLayer() override; @@ -114,25 +127,13 @@ class TILEDSHARED_EXPORT ImageLayer : public Layer */ bool isEmpty() const override; - /** - * Returns true if the image of this layer repeats along the X axis. - */ - bool repeatX() const { return mRepeatX; } - - /** - * Returns true if the image of this layer repeats along the Y axis. - */ - bool repeatY() const { return mRepeatY; } - - /** - * Sets whether the image of this layer repeats along the X axis. - */ - void setRepeatX(bool repeatX) { mRepeatX = repeatX; } + bool repeatX() const { return mRepetition & RepeatX; } + bool repeatY() const { return mRepetition & RepeatY; } + RepetitionFlags repetition() const { return mRepetition; } - /** - * Sets whether the image of this layer repeats along the Y axis. - */ - void setRepeatY(bool repeatY) { mRepeatY = repeatY; } + void setRepeatX(bool repeatX) { mRepetition.setFlag(RepeatX, repeatX); } + void setRepeatY(bool repeatY) { mRepetition.setFlag(RepeatY, repeatY); } + void setRepetition(RepetitionFlags repetition) { mRepetition = repetition; } ImageLayer *clone() const override; @@ -143,8 +144,10 @@ class TILEDSHARED_EXPORT ImageLayer : public Layer QUrl mImageSource; QColor mTransparentColor; QPixmap mImage; - bool mRepeatX = false; - bool mRepeatY = false; + RepetitionFlags mRepetition; }; } // namespace Tiled + +Q_DECLARE_METATYPE(Tiled::ImageLayer::RepetitionFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::ImageLayer::RepetitionFlags) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 3f40d76ffe..506fa84fd0 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -48,6 +48,7 @@ #include "wangoverlay.h" #include +#include #include #include #include @@ -220,6 +221,49 @@ class ObjectRefProperty : public PropertyTemplate }; +class ImageLayerRepeatProperty : public PropertyTemplate +{ + Q_OBJECT + +public: + using PropertyTemplate::PropertyTemplate; + + QWidget *createEditor(QWidget *parent) override + { + auto editor = new QWidget(parent); + auto layout = new QHBoxLayout(editor); + auto repeatX = new QCheckBox(tr("X"), editor); + auto repeatY = new QCheckBox(tr("Y"), editor); + layout->setContentsMargins(QMargins()); + layout->addWidget(repeatX); + layout->addWidget(repeatY); + + auto syncEditor = [=] { + const QSignalBlocker xBlocker(repeatX); + const QSignalBlocker yBlocker(repeatY); + const auto v = value(); + repeatX->setChecked(v & ImageLayer::RepeatX); + repeatY->setChecked(v & ImageLayer::RepeatY); + }; + auto syncProperty = [=] { + ImageLayer::RepetitionFlags v; + if (repeatX->isChecked()) + v |= ImageLayer::RepeatX; + if (repeatY->isChecked()) + v |= ImageLayer::RepeatY; + setValue(v); + }; + + syncEditor(); + + connect(this, &Property::valueChanged, editor, syncEditor); + connect(repeatX, &QCheckBox::toggled, this, syncProperty); + connect(repeatY, &QCheckBox::toggled, this, syncProperty); + return editor; + } +}; + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mScrollArea(new QScrollArea(this)) @@ -231,7 +275,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mPropertyBrowser = new VariantEditor(scrollWidget); verticalLayout->addWidget(mPropertyBrowser); verticalLayout->addStretch(); - verticalLayout->setContentsMargins(QMargins()); + verticalLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(4)); mScrollArea->setWidget(scrollWidget); mScrollArea->setWidgetResizable(true); @@ -402,6 +446,7 @@ class ClassProperty : public StringProperty Object *mObject; }; + class MapSizeProperty : public SizeProperty { Q_OBJECT @@ -898,27 +943,23 @@ class ImageLayerProperties : public LayerProperties push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value)); }); - // todo: consider merging Repeat X and Y into a single property - mRepeatXProperty = new BoolProperty( - tr("Repeat X"), - [this] { return imageLayer()->repeatX(); }, - [this](const bool &value) { - push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, value)); - }); - - mRepeatYProperty = new BoolProperty( - tr("Repeat Y"), - [this] { return imageLayer()->repeatY(); }, - [this](const bool &value) { - push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, value)); + mRepeatProperty = new ImageLayerRepeatProperty( + tr("Repeat"), + [this] { return imageLayer()->repetition(); }, + [this](const ImageLayer::RepetitionFlags &value) { + const bool repeatX = value & ImageLayer::RepeatX; + const bool repeatY = value & ImageLayer::RepeatY; + if (repeatX != imageLayer()->repeatX()) + push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, repeatX)); + if (repeatY != imageLayer()->repeatY()) + push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, repeatY)); }); mImageLayerProperties = new GroupProperty(tr("Image Layer")); mImageLayerProperties->addProperty(mImageProperty); mImageLayerProperties->addProperty(mTransparentColorProperty); mImageLayerProperties->addSeparator(); - mImageLayerProperties->addProperty(mRepeatXProperty); - mImageLayerProperties->addProperty(mRepeatYProperty); + mImageLayerProperties->addProperty(mRepeatProperty); } void populateEditor(VariantEditor *editor) override @@ -944,8 +985,7 @@ class ImageLayerProperties : public LayerProperties if (layerChange.properties & ImageLayerChangeEvent::TransparentColorProperty) emit mTransparentColorProperty->valueChanged(); if (layerChange.properties & ImageLayerChangeEvent::RepeatProperty) { - emit mRepeatXProperty->valueChanged(); - emit mRepeatYProperty->valueChanged(); + emit mRepeatProperty->valueChanged(); } } @@ -957,8 +997,7 @@ class ImageLayerProperties : public LayerProperties GroupProperty *mImageLayerProperties; UrlProperty *mImageProperty; Property *mTransparentColorProperty; - Property *mRepeatXProperty; - Property *mRepeatYProperty; + Property *mRepeatProperty; }; class ObjectGroupProperties : public LayerProperties diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index e779971053..b6f3136c1c 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -474,8 +474,8 @@ void ElidingLabel::paintEvent(QPaintEvent *) setToolTip(isElided ? text() : QString()); } - QPainter painter(this); - QWidget::style()->drawItemText(&painter, cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); + QStylePainter p(this); + p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); } @@ -486,11 +486,10 @@ HeaderWidget::HeaderWidget(const QString &text, QWidget *parent) setForegroundRole(QPalette::BrightText); setAutoFillBackground(true); - const int verticalMargin = Utils::dpiScaled(3); - const int horizontalMargin = Utils::dpiScaled(6); + const int spacing = Utils::dpiScaled(4); const int branchIndicatorWidth = Utils::dpiScaled(14); - setContentsMargins(horizontalMargin + branchIndicatorWidth, - verticalMargin, horizontalMargin, verticalMargin); + setContentsMargins(spacing + branchIndicatorWidth, + spacing, spacing, spacing); } void HeaderWidget::mousePressEvent(QMouseEvent *event) @@ -503,13 +502,9 @@ void HeaderWidget::mousePressEvent(QMouseEvent *event) ElidingLabel::mousePressEvent(event); } -void HeaderWidget::paintEvent(QPaintEvent *) +void HeaderWidget::paintEvent(QPaintEvent *event) { - const QRect cr = contentsRect(); - const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; - const int align = QStyle::visualAlignment(dir, {}); - const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight - : Qt::TextForceRightToLeft); + ElidingLabel::paintEvent(event); QStyleOption branchOption; branchOption.initFrom(this); @@ -520,13 +515,6 @@ void HeaderWidget::paintEvent(QPaintEvent *) QStylePainter p(this); p.drawPrimitive(QStyle::PE_IndicatorBranch, branchOption); - - QStyleOption opt; - opt.initFrom(this); - - const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); - - p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); } diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 9c8362182d..2b2024d65c 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -681,7 +681,7 @@ Property *PropertyFactory::createProperty(const QString &name, const auto type = get().userType(); switch (type) { case QMetaType::QString: - return createTypedProperty(name, get, set); + return createTypedProperty(name, get, set); case QMetaType::QUrl: return createTypedProperty(name, get, set); case QMetaType::Int: From e1416bda0b278915488c3e2004137481f4c18198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 16 Sep 2024 13:27:07 +0200 Subject: [PATCH 21/78] Added support for "no label" properties, used for bool values Also fixed indentatation of property labels to match the header widget. And fixed the margins on the side for groups of properties without separator (affected Custom Properties). Not entirely sure moving the label of boolean properties is the right thing to do. --- src/tiled/varianteditor.cpp | 66 ++++++++++++++++++------------------- src/tiled/varianteditor.h | 14 ++++---- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 2b2024d65c..0f978e16ee 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -152,20 +152,16 @@ QWidget *FloatProperty::createEditor(QWidget *parent) QWidget *BoolProperty::createEditor(QWidget *parent) { - auto editor = new QCheckBox(parent); + auto editor = new QCheckBox(name(), parent); auto syncEditor = [=] { const QSignalBlocker blocker(editor); bool checked = value(); editor->setChecked(checked); - editor->setText(checked ? tr("On") : tr("Off")); }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, &QCheckBox::toggled, this, [=](bool checked) { - editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); - setValue(checked); - }); + connect(editor, &QCheckBox::toggled, this, &BoolProperty::setValue); return editor; } @@ -452,16 +448,10 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) VariantEditor::VariantEditor(QWidget *parent) : QWidget(parent) { - m_gridLayout = new QGridLayout(this); - - m_gridLayout->setContentsMargins(0, 0, 0, Utils::dpiScaled(3)); - m_gridLayout->setSpacing(Utils::dpiScaled(3)); + m_layout = new QVBoxLayout(this); - m_gridLayout->setColumnStretch(LabelColumn, 2); - m_gridLayout->setColumnStretch(WidgetColumn, 3); - m_gridLayout->setColumnMinimumWidth(LeftSpacing, Utils::dpiScaled(3)); - m_gridLayout->setColumnMinimumWidth(MiddleSpacing, Utils::dpiScaled(2)); - m_gridLayout->setColumnMinimumWidth(RightSpacing, Utils::dpiScaled(3)); + m_layout->setContentsMargins(QMargins()); + m_layout->setSpacing(Utils::dpiScaled(4)); // setValue(QVariantMap { // { QStringLiteral("Name"), QVariant(QLatin1String("Hello")) }, @@ -485,7 +475,7 @@ VariantEditor::VariantEditor(QWidget *parent) void VariantEditor::clear() { QLayoutItem *item; - while ((item = m_gridLayout->takeAt(0))) { + while ((item = m_layout->takeAt(0))) { delete item->widget(); delete item; } @@ -496,9 +486,7 @@ HeaderWidget *VariantEditor::addHeader(const QString &text) { auto headerWidget = new HeaderWidget(text, this); - m_gridLayout->addWidget(headerWidget, m_rowIndex, 0, 1, ColumnCount); - - ++m_rowIndex; + m_layout->addWidget(headerWidget); return headerWidget; } @@ -509,31 +497,44 @@ void VariantEditor::addSeparator() separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); - m_gridLayout->addWidget(separator, m_rowIndex, 0, 1, ColumnCount); - ++m_rowIndex; + m_layout->addWidget(separator); } void VariantEditor::addProperty(Property *property) { + const int spacing = Utils::dpiScaled(4); + const int branchIndicatorWidth = Utils::dpiScaled(14); + switch (property->displayMode()) { - case Property::DisplayMode::Default: { - auto label = new LineEditLabel(property->name(), this); - label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - label->setToolTip(property->toolTip()); - label->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, label, &QLabel::setEnabled); - m_gridLayout->addWidget(label, m_rowIndex, LabelColumn, Qt::AlignTop/* | Qt::AlignRight*/); + case Property::DisplayMode::Default: + case Property::DisplayMode::NoLabel: { + auto propertyLayout = new QHBoxLayout; + propertyLayout->setContentsMargins(spacing, 0, spacing, 0); + propertyLayout->setSpacing(spacing); + + if (property->displayMode() == Property::DisplayMode::Default) { + auto label = new LineEditLabel(property->name(), this); + label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + label->setToolTip(property->toolTip()); + label->setEnabled(property->isEnabled()); + label->setContentsMargins(branchIndicatorWidth, 0, 0, 0); + connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); + propertyLayout->addWidget(label, LabelStretch, Qt::AlignTop); + } else { + propertyLayout->addStretch(LabelStretch); + } if (auto editor = createEditor(property)) { editor->setToolTip(property->toolTip()); editor->setEnabled(property->isEnabled()); connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); - m_gridLayout->addWidget(editor, m_rowIndex, WidgetColumn); + propertyLayout->addWidget(editor, WidgetStretch); } - ++m_rowIndex; + m_layout->addLayout(propertyLayout); + break; } case Property::DisplayMode::Header: { @@ -548,8 +549,7 @@ void VariantEditor::addProperty(Property *property) layout()->activate(); }); - m_gridLayout->addWidget(editor, m_rowIndex, 0, 1, ColumnCount); - ++m_rowIndex; + m_layout->addWidget(editor); } break; diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 37afb146e3..b60e53860b 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -28,7 +28,7 @@ #include #include -class QGridLayout; +class QVBoxLayout; namespace Tiled { @@ -47,6 +47,7 @@ class Property : public QObject public: enum class DisplayMode { Default, + NoLabel, Header, Separator }; @@ -209,6 +210,7 @@ struct FloatProperty : PropertyTemplate struct BoolProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; + DisplayMode displayMode() const override { return DisplayMode::NoLabel; } QWidget *createEditor(QWidget *parent) override; }; @@ -393,15 +395,11 @@ class VariantEditor : public QWidget QWidget *createEditor(Property *property); enum Column { - LeftSpacing, - LabelColumn, - MiddleSpacing, - WidgetColumn, - RightSpacing, - ColumnCount, + LabelStretch = 2, + WidgetStretch = 3, }; - QGridLayout *m_gridLayout; + QVBoxLayout *m_layout; int m_rowIndex = 0; }; From 6bbae7f044aaca62cc30a128733339679492075d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 16 Sep 2024 14:19:31 +0200 Subject: [PATCH 22/78] Used Slider for opacity property, replacing the one in Layers view Opacity is now implemented as IntProperty that goes from 0 to 100. Now the Properties view needs to be open to change the layer opacity. If that's a problem for some we can consider reintroducing the opacity slider in the Layers view. --- src/tiled/layerdock.cpp | 90 ++-------------------------------- src/tiled/layerdock.h | 9 ---- src/tiled/propertieswidget.cpp | 16 +++--- src/tiled/varianteditor.cpp | 43 +++++++++++----- src/tiled/varianteditor.h | 4 +- 5 files changed, 45 insertions(+), 117 deletions(-) diff --git a/src/tiled/layerdock.cpp b/src/tiled/layerdock.cpp index 4810d0367d..f6dd6b3abc 100644 --- a/src/tiled/layerdock.cpp +++ b/src/tiled/layerdock.cpp @@ -29,7 +29,6 @@ #include "map.h" #include "mapdocument.h" #include "mapdocumentactionhandler.h" -#include "objectgroup.h" #include "reversingproxymodel.h" #include "utils.h" #include "iconcheckdelegate.h" @@ -39,10 +38,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -54,8 +51,6 @@ using namespace Tiled; LayerDock::LayerDock(QWidget *parent): QDockWidget(parent), - mOpacityLabel(new QLabel), - mOpacitySlider(new QSlider(Qt::Horizontal)), mLayerView(new LayerView) { setObjectName(QLatin1String("layerDock")); @@ -64,13 +59,6 @@ LayerDock::LayerDock(QWidget *parent): QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); - QHBoxLayout *opacityLayout = new QHBoxLayout; - mOpacitySlider->setRange(0, 100); - mOpacitySlider->setEnabled(false); - opacityLayout->addWidget(mOpacityLabel); - opacityLayout->addWidget(mOpacitySlider); - mOpacityLabel->setBuddy(mOpacitySlider); - MapDocumentActionHandler *handler = MapDocumentActionHandler::instance(); QMenu *newLayerMenu = handler->createNewLayerMenu(this); @@ -99,20 +87,12 @@ LayerDock::LayerDock(QWidget *parent): buttonContainer->addWidget(spacerWidget); buttonContainer->addAction(ActionManager::action("HighlightCurrentLayer")); - QVBoxLayout *listAndToolBar = new QVBoxLayout; - listAndToolBar->setSpacing(0); - listAndToolBar->addWidget(mLayerView); - listAndToolBar->addWidget(buttonContainer); - - layout->addLayout(opacityLayout); - layout->addLayout(listAndToolBar); + layout->setSpacing(0); + layout->addWidget(mLayerView); + layout->addWidget(buttonContainer); setWidget(widget); retranslateUi(); - - connect(mOpacitySlider, &QAbstractSlider::valueChanged, - this, &LayerDock::sliderValueChanged); - updateOpacitySlider(); } void LayerDock::setMapDocument(MapDocument *mapDocument) @@ -126,10 +106,6 @@ void LayerDock::setMapDocument(MapDocument *mapDocument) mMapDocument = mapDocument; if (mMapDocument) { - connect(mMapDocument, &MapDocument::changed, - this, &LayerDock::documentChanged); - connect(mMapDocument, &MapDocument::currentLayerChanged, - this, &LayerDock::updateOpacitySlider); connect(mMapDocument, &MapDocument::editLayerNameRequested, this, &LayerDock::editLayerName); } @@ -145,8 +121,6 @@ void LayerDock::setMapDocument(MapDocument *mapDocument) mLayerView->header()->resizeSection(1, iconSectionWidth); mLayerView->header()->resizeSection(2, iconSectionWidth); } - - updateOpacitySlider(); } void LayerDock::changeEvent(QEvent *e) @@ -162,40 +136,6 @@ void LayerDock::changeEvent(QEvent *e) } } -void LayerDock::updateOpacitySlider() -{ - const bool enabled = mMapDocument && - mMapDocument->currentLayer() != nullptr; - - mOpacitySlider->setEnabled(enabled); - mOpacityLabel->setEnabled(enabled); - - QScopedValueRollback updating(mUpdatingSlider, true); - if (enabled) { - qreal opacity = mMapDocument->currentLayer()->opacity(); - mOpacitySlider->setValue(qRound(opacity * 100)); - } else { - mOpacitySlider->setValue(100); - } -} - -void LayerDock::documentChanged(const ChangeEvent &change) -{ - switch (change.type) { - case ChangeEvent::LayerChanged: { - auto &layerChange = static_cast(change); - - // Don't update the slider when we're the ones changing the layer opacity - if ((layerChange.properties & LayerChangeEvent::OpacityProperty) && !mChangingLayerOpacity) - if (layerChange.layer == mMapDocument->currentLayer()) - updateOpacitySlider(); - break; - } - default: - break; - } -} - void LayerDock::editLayerName() { if (!isVisible()) @@ -208,33 +148,9 @@ void LayerDock::editLayerName() mLayerView->editLayerModelIndex(layerModel->index(currentLayer)); } -void LayerDock::sliderValueChanged(int opacity) -{ - if (!mMapDocument) - return; - - // When the slider changes value just because we're updating it, it - // shouldn't try to set the layer opacity. - if (mUpdatingSlider) - return; - - const auto layer = mMapDocument->currentLayer(); - if (!layer) - return; - - if (static_cast(layer->opacity() * 100) != opacity) { - LayerModel *layerModel = mMapDocument->layerModel(); - QScopedValueRollback updating(mChangingLayerOpacity, true); - layerModel->setData(layerModel->index(layer), - qreal(opacity) / 100, - LayerModel::OpacityRole); - } -} - void LayerDock::retranslateUi() { setWindowTitle(tr("Layers")); - mOpacityLabel->setText(tr("Opacity:")); mNewLayerButton->setToolTip(tr("New Layer")); } diff --git a/src/tiled/layerdock.h b/src/tiled/layerdock.h index 41e286c530..2b1692750d 100644 --- a/src/tiled/layerdock.h +++ b/src/tiled/layerdock.h @@ -28,9 +28,7 @@ #include class QAbstractProxyModel; -class QLabel; class QModelIndex; -class QUndoStack; namespace Tiled { @@ -58,20 +56,13 @@ class LayerDock : public QDockWidget void changeEvent(QEvent *e) override; private: - void updateOpacitySlider(); - void documentChanged(const ChangeEvent &change); void editLayerName(); - void sliderValueChanged(int opacity); void retranslateUi(); - QLabel *mOpacityLabel; - QSlider *mOpacitySlider; QToolButton *mNewLayerButton; LayerView *mLayerView; MapDocument *mMapDocument = nullptr; - bool mUpdatingSlider = false; - bool mChangingLayerOpacity = false; }; /** diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 506fa84fd0..f586ab5e68 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -822,15 +822,15 @@ class LayerProperties : public ObjectProperties push(new SetLayerLocked(mapDocument(), { layer() }, value)); }); - // todo: would be nice to use a slider (replacing the one in Layers view) - mOpacityProperty = new FloatProperty( + mOpacityProperty = new IntProperty( tr("Opacity"), - [this] { return layer()->opacity(); }, - [this](const double &value) { - push(new SetLayerOpacity(mapDocument(), { layer() }, value)); + [this] { return qRound(layer()->opacity() * 100); }, + [this](const int &value) { + push(new SetLayerOpacity(mapDocument(), { layer() }, qreal(value) / 100)); }); - mOpacityProperty->setRange(0.0, 1.0); - mOpacityProperty->setSingleStep(0.1); + mOpacityProperty->setRange(0, 100); + mOpacityProperty->setSuffix(tr("%")); + mOpacityProperty->setSliderEnabled(true); mTintColorProperty = new ColorProperty( tr("Tint Color"), @@ -914,7 +914,7 @@ class LayerProperties : public ObjectProperties Property *mNameProperty; Property *mVisibleProperty; Property *mLockedProperty; - FloatProperty *mOpacityProperty; + IntProperty *mOpacityProperty; Property *mTintColorProperty; Property *mOffsetProperty; PointFProperty *mParallaxFactorProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 0f978e16ee..4b87873ef1 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -112,22 +112,41 @@ QWidget *UrlProperty::createEditor(QWidget *parent) QWidget *IntProperty::createEditor(QWidget *parent) { - auto editor = new SpinBox(parent); - editor->setRange(m_minimum, m_maximum); - editor->setSingleStep(m_singleStep); - editor->setSuffix(m_suffix); + auto widget = new QWidget(parent); + auto layout = new QHBoxLayout(widget); + layout->setContentsMargins(QMargins()); - auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - editor->setValue(value()); - }; - syncEditor(); + if (m_sliderEnabled) { + QSlider *slider = new QSlider(Qt::Horizontal, widget); + slider->setRange(m_minimum, m_maximum); + slider->setSingleStep(m_singleStep); + slider->setValue(value()); - connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, qOverload(&SpinBox::valueChanged), + layout->addWidget(slider); + + connect(this, &Property::valueChanged, slider, [this, slider] { + const QSignalBlocker blocker(slider); + slider->setValue(value()); + }); + connect(slider, &QSlider::valueChanged, this, &IntProperty::setValue); + } + + auto spinBox = new SpinBox(parent); + spinBox->setRange(m_minimum, m_maximum); + spinBox->setSingleStep(m_singleStep); + spinBox->setSuffix(m_suffix); + spinBox->setValue(value()); + + layout->addWidget(spinBox); + + connect(this, &Property::valueChanged, spinBox, [this, spinBox] { + const QSignalBlocker blocker(spinBox); + spinBox->setValue(value()); + }); + connect(spinBox, qOverload(&SpinBox::valueChanged), this, &IntProperty::setValue); - return editor; + return widget; } QWidget *FloatProperty::createEditor(QWidget *parent) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index b60e53860b..26f5d8cbba 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -177,12 +177,14 @@ struct IntProperty : PropertyTemplate setMinimum(minimum); setMaximum(maximum); } + void setSliderEnabled(bool enabled) { m_sliderEnabled = enabled; } -private: +protected: int m_minimum = std::numeric_limits::min(); int m_maximum = std::numeric_limits::max(); int m_singleStep = 1; QString m_suffix; + bool m_sliderEnabled = false; }; struct FloatProperty : PropertyTemplate From 3daeedcecc99700cb4651a69de43abe13ede257d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 16 Sep 2024 21:25:02 +0200 Subject: [PATCH 23/78] Made the property label indentation shrink when there is very little space --- src/tiled/varianteditor.cpp | 8 ++++++-- src/tiled/varianteditor.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 4b87873ef1..4a088469a8 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -528,15 +528,19 @@ void VariantEditor::addProperty(Property *property) case Property::DisplayMode::Default: case Property::DisplayMode::NoLabel: { auto propertyLayout = new QHBoxLayout; - propertyLayout->setContentsMargins(spacing, 0, spacing, 0); + propertyLayout->setContentsMargins(0, 0, spacing, 0); propertyLayout->setSpacing(spacing); + // Property label indentation, which shrinks when there is very little space + propertyLayout->addSpacerItem(new QSpacerItem(branchIndicatorWidth + spacing, 0, + QSizePolicy::Maximum)); + propertyLayout->setStretch(0, 1); + if (property->displayMode() == Property::DisplayMode::Default) { auto label = new LineEditLabel(property->name(), this); label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); label->setToolTip(property->toolTip()); label->setEnabled(property->isEnabled()); - label->setContentsMargins(branchIndicatorWidth, 0, 0, 0); connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); propertyLayout->addWidget(label, LabelStretch, Qt::AlignTop); diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 26f5d8cbba..6abcace58b 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -397,8 +397,8 @@ class VariantEditor : public QWidget QWidget *createEditor(Property *property); enum Column { - LabelStretch = 2, - WidgetStretch = 3, + LabelStretch = 4, + WidgetStretch = 6, }; QVBoxLayout *m_layout; From 057edb4a876f4275c1d48692c88d50ededcb4a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 17 Sep 2024 13:58:10 +0200 Subject: [PATCH 24/78] Implemented custom widget for allowed transformations --- src/tiled/propertieswidget.cpp | 95 +++++++++++++++++++++++++++-- src/tiled/propertyeditorwidgets.cpp | 2 +- src/tiled/varianteditor.cpp | 4 +- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index f586ab5e68..a4317f322c 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -264,6 +265,90 @@ class ImageLayerRepeatProperty : public PropertyTemplate +{ + Q_OBJECT + +public: + using PropertyTemplate::PropertyTemplate; + + QWidget *createEditor(QWidget *parent) override + { + QIcon flipHorizontalIcon(QLatin1String(":images/24/flip-horizontal.png")); + QIcon flipVerticalIcon(QLatin1String(":images/24/flip-vertical.png")); + QIcon rotateRightIcon(QLatin1String(":images/24/rotate-right.png")); + + flipHorizontalIcon.addFile(QLatin1String(":images/32/flip-horizontal.png")); + flipVerticalIcon.addFile(QLatin1String(":images/32/flip-vertical.png")); + rotateRightIcon.addFile(QLatin1String(":images/32/rotate-right.png")); + + auto editor = new QWidget(parent); + + auto flipHorizontally = new QToolButton(editor); + flipHorizontally->setToolTip(tr("Flip Horizontally")); + flipHorizontally->setIcon(flipHorizontalIcon); + flipHorizontally->setCheckable(true); + + auto flipVertically = new QToolButton(editor); + flipVertically->setToolTip(tr("Flip Vertically")); + flipVertically->setIcon(flipVerticalIcon); + flipVertically->setCheckable(true); + + auto rotate = new QToolButton(editor); + rotate->setToolTip(tr("Rotate")); + rotate->setIcon(rotateRightIcon); + rotate->setCheckable(true); + + auto preferUntransformed = new QCheckBox(tr("Prefer Untransformed"), editor); + + auto horizontalLayout = new QHBoxLayout; + horizontalLayout->addWidget(flipHorizontally); + horizontalLayout->addWidget(flipVertically); + horizontalLayout->addWidget(rotate); + horizontalLayout->addStretch(); + + auto verticalLayout = new QVBoxLayout(editor); + verticalLayout->setContentsMargins(QMargins()); + verticalLayout->setSpacing(Utils::dpiScaled(4)); + verticalLayout->addLayout(horizontalLayout); + verticalLayout->addWidget(preferUntransformed); + + auto syncEditor = [=] { + const QSignalBlocker horizontalBlocker(flipHorizontally); + const QSignalBlocker verticalBlocker(flipVertically); + const QSignalBlocker rotateBlocker(rotate); + const QSignalBlocker preferUntransformedBlocker(preferUntransformed); + const auto v = value(); + flipHorizontally->setChecked(v & Tileset::AllowFlipHorizontally); + flipVertically->setChecked(v & Tileset::AllowFlipVertically); + rotate->setChecked(v & Tileset::AllowRotate); + preferUntransformed->setChecked(v & Tileset::PreferUntransformed); + }; + auto syncProperty = [=] { + Tileset::TransformationFlags v; + if (flipHorizontally->isChecked()) + v |= Tileset::AllowFlipHorizontally; + if (flipVertically->isChecked()) + v |= Tileset::AllowFlipVertically; + if (rotate->isChecked()) + v |= Tileset::AllowRotate; + if (preferUntransformed->isChecked()) + v |= Tileset::PreferUntransformed; + setValue(v); + }; + + syncEditor(); + + connect(this, &Property::valueChanged, editor, syncEditor); + connect(flipHorizontally, &QAbstractButton::toggled, this, syncProperty); + connect(flipVertically, &QAbstractButton::toggled, this, syncProperty); + connect(rotate, &QAbstractButton::toggled, this, syncProperty); + connect(preferUntransformed, &QAbstractButton::toggled, this, syncProperty); + return editor; + } +}; + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mScrollArea(new QScrollArea(this)) @@ -1149,15 +1234,13 @@ class TilesetProperties : public ObjectProperties }); mColumnCountProperty->setMinimum(1); - // todo: this needs a custom widget - mAllowedTransformationsProperty = new IntProperty( + mAllowedTransformationsProperty = new TransformationFlagsProperty( tr("Allowed Transformations"), [this] { - return static_cast(tileset()->transformationFlags()); + return tileset()->transformationFlags(); }, - [this](const int &value) { - const auto flags = static_cast(value); - push(new ChangeTilesetTransformationFlags(tilesetDocument(), flags)); + [this](const Tileset::TransformationFlags &value) { + push(new ChangeTilesetTransformationFlags(tilesetDocument(), value)); }); // todo: this needs a custom widget diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index b6f3136c1c..d57d0fcce6 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -121,7 +121,7 @@ ResponsivePairswiseWidget::ResponsivePairswiseWidget(QWidget *parent) auto layout = new QGridLayout(this); layout->setContentsMargins(QMargins()); layout->setColumnStretch(1, 1); - layout->setSpacing(Utils::dpiScaled(3)); + layout->setSpacing(Utils::dpiScaled(4)); } void ResponsivePairswiseWidget::setWidgetPairs(const QVector &widgetPairs) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 4a088469a8..c5f96776ac 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -355,7 +355,7 @@ QWidget *FontProperty::createEditor(QWidget *parent) auto layout = new QVBoxLayout(editor); layout->setContentsMargins(QMargins()); - layout->setSpacing(Utils::dpiScaled(3)); + layout->setSpacing(Utils::dpiScaled(4)); layout->addWidget(fontComboBox); layout->addWidget(sizeSpinBox); @@ -418,7 +418,7 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) auto editor = new QWidget(parent); auto layout = new QGridLayout(editor); layout->setContentsMargins(QMargins()); - layout->setSpacing(Utils::dpiScaled(3)); + layout->setSpacing(Utils::dpiScaled(4)); auto horizontalLabel = new ElidingLabel(tr("Horizontal"), editor); layout->addWidget(horizontalLabel, 0, 0); From 0bc17ed42dcd9a667fc053654cac19eb5ff7d7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 17 Sep 2024 14:10:10 +0200 Subject: [PATCH 25/78] Fixed an issue with VariantEditor::clear Not everything was deleted from the layout because the deletion was lacking support for handling nested layouts. Also removed the only remaining case where a nested layout was used at the top-level. --- src/tiled/propertieswidget.cpp | 9 ++++++--- src/tiled/scriptdialog.cpp | 17 ++--------------- src/tiled/utils.cpp | 17 +++++++++++++++++ src/tiled/utils.h | 3 +++ src/tiled/varianteditor.cpp | 24 +++++------------------- src/tiled/varianteditor.h | 2 -- 6 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index a4317f322c..58b0f842ca 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1106,14 +1106,16 @@ class ObjectGroupProperties : public LayerProperties [this](ObjectGroup::DrawOrder value) { push(new ChangeObjectGroupDrawOrder(mapDocument(), { objectGroup() }, value)); }); + + mObjectGroupProperties = new GroupProperty(tr("Object Layer")); + mObjectGroupProperties->addProperty(mColorProperty); + mObjectGroupProperties->addProperty(mDrawOrderProperty); } void populateEditor(VariantEditor *editor) override { LayerProperties::populateEditor(editor); - editor->addHeader(tr("Object Layer")); - editor->addProperty(mColorProperty); - editor->addProperty(mDrawOrderProperty); + editor->addProperty(mObjectGroupProperties); } private: @@ -1139,6 +1141,7 @@ class ObjectGroupProperties : public LayerProperties return static_cast(mObject); } + GroupProperty *mObjectGroupProperties; Property *mColorProperty; Property *mDrawOrderProperty; }; diff --git a/src/tiled/scriptdialog.cpp b/src/tiled/scriptdialog.cpp index 0c8b64afb3..b92a993ac0 100644 --- a/src/tiled/scriptdialog.cpp +++ b/src/tiled/scriptdialog.cpp @@ -26,6 +26,7 @@ #include "mainwindow.h" #include "scriptimage.h" #include "scriptmanager.h" +#include "utils.h" #include #include @@ -41,8 +42,6 @@ #include #include -#include - static const int leftColumnStretch = 0; // stretch as much as we can so that the left column looks as close to zero width as possible when there is no content static const int rightColumnStretch = 1; @@ -51,18 +50,6 @@ namespace Tiled { QSet ScriptDialog::sDialogInstances; -static void deleteAllFromLayout(QLayout *layout) -{ - while (QLayoutItem *item = layout->takeAt(0)) { - delete item->widget(); - - if (QLayout *layout = item->layout()) - deleteAllFromLayout(layout); - - delete item; - } -} - ScriptImageWidget::ScriptImageWidget(Tiled::ScriptImage *image, QWidget *parent) : QLabel(parent) { @@ -144,7 +131,7 @@ void ScriptDialog::initializeLayout() void ScriptDialog::clear() { - deleteAllFromLayout(layout()); + Utils::deleteAllFromLayout(layout()); initializeLayout(); } diff --git a/src/tiled/utils.cpp b/src/tiled/utils.cpp index be6ae66ad6..ac7538ccc3 100644 --- a/src/tiled/utils.cpp +++ b/src/tiled/utils.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include #include @@ -607,5 +609,20 @@ QString Error::jsonParseError(QJsonParseError error) } +/** + * Recursively deletes all items and their widgets from the given layout. + */ +void deleteAllFromLayout(QLayout *layout) +{ + while (QLayoutItem *item = layout->takeAt(0)) { + delete item->widget(); + + if (QLayout *layout = item->layout()) + deleteAllFromLayout(layout); + + delete item; + } +} + } // namespace Utils } // namespace Tiled diff --git a/src/tiled/utils.h b/src/tiled/utils.h index 3e73500b3a..e7c9721015 100644 --- a/src/tiled/utils.h +++ b/src/tiled/utils.h @@ -33,6 +33,7 @@ class QAction; class QKeyEvent; +class QLayout; class QMenu; namespace Tiled { @@ -111,5 +112,7 @@ namespace Error { QString jsonParseError(QJsonParseError error); } // namespace Error +void deleteAllFromLayout(QLayout *layout); + } // namespace Utils } // namespace Tiled diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index c5f96776ac..598220aed1 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -493,21 +493,7 @@ VariantEditor::VariantEditor(QWidget *parent) void VariantEditor::clear() { - QLayoutItem *item; - while ((item = m_layout->takeAt(0))) { - delete item->widget(); - delete item; - } - m_rowIndex = 0; -} - -HeaderWidget *VariantEditor::addHeader(const QString &text) -{ - auto headerWidget = new HeaderWidget(text, this); - - m_layout->addWidget(headerWidget); - - return headerWidget; + Utils::deleteAllFromLayout(m_layout); } void VariantEditor::addSeparator() @@ -521,7 +507,7 @@ void VariantEditor::addSeparator() void VariantEditor::addProperty(Property *property) { - const int spacing = Utils::dpiScaled(4); + const int spacing = m_layout->spacing(); const int branchIndicatorWidth = Utils::dpiScaled(14); switch (property->displayMode()) { @@ -529,10 +515,9 @@ void VariantEditor::addProperty(Property *property) case Property::DisplayMode::NoLabel: { auto propertyLayout = new QHBoxLayout; propertyLayout->setContentsMargins(0, 0, spacing, 0); - propertyLayout->setSpacing(spacing); // Property label indentation, which shrinks when there is very little space - propertyLayout->addSpacerItem(new QSpacerItem(branchIndicatorWidth + spacing, 0, + propertyLayout->addSpacerItem(new QSpacerItem(spacing + branchIndicatorWidth, 0, QSizePolicy::Maximum)); propertyLayout->setStretch(0, 1); @@ -561,7 +546,8 @@ void VariantEditor::addProperty(Property *property) break; } case Property::DisplayMode::Header: { - auto headerWidget = addHeader(property->name()); + auto headerWidget = new HeaderWidget(property->name(), this); + m_layout->addWidget(headerWidget); if (auto editor = createEditor(property)) { connect(headerWidget, &HeaderWidget::toggled, diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 6abcace58b..3dcdf86dec 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -388,7 +388,6 @@ class VariantEditor : public QWidget VariantEditor(QWidget *parent = nullptr); void clear(); - HeaderWidget *addHeader(const QString &text); void addSeparator(); void addProperty(Property *property); // void addValue(const QVariant &value); @@ -402,7 +401,6 @@ class VariantEditor : public QWidget }; QVBoxLayout *m_layout; - int m_rowIndex = 0; }; } // namespace Tiled From 82da15c23e2e4a7fc0d34fc00362d47e4dadfd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 17 Sep 2024 17:45:44 +0200 Subject: [PATCH 26/78] Added suffixes, support for custom enums and other progress * Added "px" suffixes to various pixel values. * Added support for custom enum properties (top-level only so far, because there is no support for class properties yet). It's amazing how trivial this was with the new approach (though, it does not support enums with values as flags yet). * Enabled editing of tileset image parameters through a button and made some progress towards allowing a GroupProperty to appear like a normal property (currently it can, but then it no longer shows its children). --- src/tiled/propertieswidget.cpp | 123 ++++++++++++++++++++++++---- src/tiled/propertyeditorwidgets.cpp | 12 +++ src/tiled/propertyeditorwidgets.h | 3 + src/tiled/tilesetparametersedit.cpp | 2 +- src/tiled/varianteditor.cpp | 19 ++--- src/tiled/varianteditor.h | 11 ++- 6 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 58b0f842ca..9a8f54a532 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -42,6 +42,7 @@ #include "propertybrowser.h" #include "tilesetchanges.h" #include "tilesetdocument.h" +#include "tilesetparametersedit.h" #include "utils.h" #include "varianteditor.h" #include "variantpropertymanager.h" @@ -349,6 +350,30 @@ class TransformationFlagsProperty : public PropertyTemplatesetTilesetDocument(mTilesetDocument); + return editor; + } + +private: + TilesetDocument *mTilesetDocument; +}; + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mScrollArea(new QScrollArea(this)) @@ -642,6 +667,7 @@ class MapProperties : public ObjectProperties }, this); mTileSizeProperty->setMinimum(1); + mTileSizeProperty->setSuffix(tr(" px")); mInfiniteProperty = new BoolProperty( tr("Infinite"), @@ -664,6 +690,7 @@ class MapProperties : public ObjectProperties Map::HexSideLengthProperty, value.toInt())); }); + mHexSideLengthProperty->setSuffix(tr(" px")); mStaggerAxisProperty = new EnumProperty( tr("Stagger Axis"), @@ -858,7 +885,7 @@ class MapProperties : public ObjectProperties Property *mSizeProperty; SizeProperty *mTileSizeProperty; Property *mInfiniteProperty; - Property *mHexSideLengthProperty; + IntProperty *mHexSideLengthProperty; Property *mStaggerAxisProperty; Property *mStaggerIndexProperty; Property *mParallaxOriginProperty; @@ -1180,6 +1207,7 @@ class TilesetProperties : public ObjectProperties [this](const QPoint &value) { push(new ChangeTilesetTileOffset(tilesetDocument(), value)); }); + mTileOffsetProperty->setSuffix(tr(" px")); mTileRenderSizeProperty = new EnumProperty( tr("Tile Render Size"), @@ -1226,6 +1254,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetGridSize(tilesetDocument(), value)); }); mGridSizeProperty->setMinimum(1); + mGridSizeProperty->setSuffix(tr(" px")); mColumnCountProperty = new IntProperty( tr("Columns"), @@ -1246,15 +1275,38 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetTransformationFlags(tilesetDocument(), value)); }); - // todo: this needs a custom widget + // todo: sub-properties are not displayed yet and image file name doesn't update in the TilesetParametersEdit + mTilesetImageProperty = new TilesetImageProperty(document, this); + mImageProperty = new UrlProperty( tr("Image"), - [this] { - return tileset()->imageSource(); - }, - [](const QUrl &) { - // push(new ChangeTilesetImage(tilesetDocument(), value.toString())); - }); + [this] { return tileset()->imageSource(); }); + + mTransparentColorProperty = new ColorProperty( + tr("Transparent Color"), + [this] { return tileset()->transparentColor(); }); + + mTileSizeProperty = new SizeProperty( + tr("Tile Size"), + [this] { return tileset()->tileSize(); }); + + mMarginProperty = new IntProperty( + tr("Margin"), + [this] { return tileset()->margin(); }); + + mTileSpacingProperty = new IntProperty( + tr("Spacing"), + [this] { return tileset()->tileSpacing(); }); + + mTileSizeProperty->setSuffix(tr(" px")); + mMarginProperty->setSuffix(tr(" px")); + mTileSpacingProperty->setSuffix(tr(" px")); + + mTilesetImageProperty->addProperty(mImageProperty); + mTilesetImageProperty->addProperty(mTransparentColorProperty); + mTilesetImageProperty->addProperty(mTileSizeProperty); + mTilesetImageProperty->addProperty(mMarginProperty); + mTilesetImageProperty->addProperty(mTileSpacingProperty); mTilesetProperties = new GroupProperty(tr("Tileset")); mTilesetProperties->addProperty(mNameProperty); @@ -1269,7 +1321,9 @@ class TilesetProperties : public ObjectProperties mTilesetProperties->addProperty(mGridSizeProperty); mTilesetProperties->addProperty(mColumnCountProperty); mTilesetProperties->addProperty(mAllowedTransformationsProperty); - mTilesetProperties->addProperty(mImageProperty); + + if (!tileset()->isCollection()) + mTilesetProperties->addProperty(mTilesetImageProperty); updateEnabledState(); connect(tilesetDocument(), &Document::changed, @@ -1316,12 +1370,21 @@ class TilesetProperties : public ObjectProperties emit mColumnCountProperty->valueChanged(); emit mAllowedTransformationsProperty->valueChanged(); emit mImageProperty->valueChanged(); + emit mTransparentColorProperty->valueChanged(); + emit mTileSizeProperty->valueChanged(); + emit mMarginProperty->valueChanged(); + emit mTileSpacingProperty->valueChanged(); } void updateEnabledState() { const bool collection = tileset()->isCollection(); + mTilesetImageProperty->setEnabled(!collection); mImageProperty->setEnabled(!collection); + mTransparentColorProperty->setEnabled(!collection); + mTileSizeProperty->setEnabled(!collection); + mMarginProperty->setEnabled(!collection); + mTileSpacingProperty->setEnabled(!collection); mColumnCountProperty->setEnabled(collection); } @@ -1338,7 +1401,7 @@ class TilesetProperties : public ObjectProperties GroupProperty *mTilesetProperties; Property *mNameProperty; Property *mObjectAlignmentProperty; - Property *mTileOffsetProperty; + PointProperty *mTileOffsetProperty; Property *mTileRenderSizeProperty; Property *mFillModeProperty; Property *mBackgroundColorProperty; @@ -1346,7 +1409,12 @@ class TilesetProperties : public ObjectProperties SizeProperty *mGridSizeProperty; IntProperty *mColumnCountProperty; Property *mAllowedTransformationsProperty; + GroupProperty *mTilesetImageProperty; Property *mImageProperty; + Property *mTransparentColorProperty; + SizeProperty *mTileSizeProperty; + IntProperty *mMarginProperty; + IntProperty *mTileSpacingProperty; }; class MapObjectProperties : public ObjectProperties @@ -2018,8 +2086,9 @@ void PropertiesWidget::currentObjectChanged(Object *object) const auto &value = it.value(); Property *property = nullptr; + auto userType = value.userType(); - switch (value.userType()) { + switch (userType) { case QMetaType::Bool: case QMetaType::QColor: case QMetaType::Double: @@ -2033,20 +2102,44 @@ void PropertiesWidget::currentObjectChanged(Object *object) break; } default: - if (value.userType() == filePathTypeId()) { + if (userType == filePathTypeId()) { auto get = [object, name] { return object->property(name).value().url; }; auto set = [this, object, name](const QUrl &value) { mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(FilePath { value }))); }; property = new UrlProperty(name, get, set); - } else if (value.userType() == objectRefTypeId()) { - auto get = [this, object, name] { return DisplayObjectRef(object->property(name).value(), static_cast(mDocument)); }; + } else if (userType == objectRefTypeId()) { + auto get = [this, object, name] { + return DisplayObjectRef(object->property(name).value(), + static_cast(mDocument)); + }; auto set = [this, object, name](const DisplayObjectRef &value) { mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(value.ref))); }; property = new ObjectRefProperty(name, get, set); + } else if (userType == propertyValueId()) { + auto propertyValue = value.value(); + if (auto propertyType = propertyValue.type()) { + switch (propertyType->type) { + case PropertyType::PT_Invalid: + break; + case PropertyType::PT_Class: + // todo: class values + break; + case PropertyType::PT_Enum: + auto enumType = static_cast(*propertyType); + // todo: support valuesAsFlags + property = new EnumProperty( + name, + [object, name] { return object->property(name).value().value.toInt(); }, + [=](int value) { + mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, propertyType->wrap(value))); + }); + static_cast*>(property)->setEnumData(enumType.values); + break; + } + } } - // todo: PropertyValue (enum and class values) break; } diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index d57d0fcce6..0d6af44cc2 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -241,6 +241,12 @@ void SizeEdit::setMinimum(int minimum) m_heightSpinBox->setMinimum(minimum); } +void SizeEdit::setSuffix(const QString &suffix) +{ + m_widthSpinBox->setSuffix(suffix); + m_heightSpinBox->setSuffix(suffix); +} + SizeFEdit::SizeFEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) @@ -299,6 +305,12 @@ QPoint PointEdit::value() const m_ySpinBox->value()); } +void PointEdit::setSuffix(const QString &suffix) +{ + m_xSpinBox->setSuffix(suffix); + m_ySpinBox->setSuffix(suffix); +} + PointFEdit::PointFEdit(QWidget *parent) : ResponsivePairswiseWidget(parent) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 9f38b945c6..493a95a02c 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -105,6 +105,7 @@ class SizeEdit : public ResponsivePairswiseWidget QSize value() const; void setMinimum(int minimum); + void setSuffix(const QString &suffix); signals: void valueChanged(); @@ -154,6 +155,8 @@ class PointEdit : public ResponsivePairswiseWidget void setValue(const QPoint &size); QPoint value() const; + void setSuffix(const QString &suffix); + signals: void valueChanged(); diff --git a/src/tiled/tilesetparametersedit.cpp b/src/tiled/tilesetparametersedit.cpp index a5138da7d0..82700fb74b 100644 --- a/src/tiled/tilesetparametersedit.cpp +++ b/src/tiled/tilesetparametersedit.cpp @@ -44,7 +44,7 @@ TilesetParametersEdit::TilesetParametersEdit(QWidget *parent) QToolButton *button = new QToolButton(this); button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred)); - button->setText(tr("Edit...")); + button->setText(tr("Edit Tileset")); layout->addWidget(mLabel); layout->addWidget(button); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 598220aed1..dff1069a4e 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -57,14 +57,6 @@ void Property::setEnabled(bool enabled) } } -QWidget *GroupProperty::createEditor(QWidget *parent) -{ - auto widget = new VariantEditor(parent); - for (auto property : std::as_const(m_subProperties)) - widget->addProperty(property); - return widget; -} - QWidget *StringProperty::createEditor(QWidget *parent) { auto editor = new QLineEdit(parent); @@ -188,6 +180,8 @@ QWidget *BoolProperty::createEditor(QWidget *parent) QWidget *PointProperty::createEditor(QWidget *parent) { auto editor = new PointEdit(parent); + editor->setSuffix(m_suffix); + auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); editor->setValue(value()); @@ -225,6 +219,7 @@ QWidget *SizeProperty::createEditor(QWidget *parent) { auto editor = new SizeEdit(parent); editor->setMinimum(m_minimum); + editor->setSuffix(m_suffix); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); @@ -549,7 +544,11 @@ void VariantEditor::addProperty(Property *property) auto headerWidget = new HeaderWidget(property->name(), this); m_layout->addWidget(headerWidget); - if (auto editor = createEditor(property)) { + if (auto groupProperty = dynamic_cast(property)) { + auto editor = new VariantEditor(this); + for (auto property : groupProperty->subProperties()) + editor->addProperty(property); + connect(headerWidget, &HeaderWidget::toggled, editor, [this, editor](bool checked) { editor->setVisible(checked); @@ -680,7 +679,7 @@ Property *createTypedProperty(const QString &name, { return new PropertyClass(name, [get = std::move(get)] { return get().value(); }, - [set = std::move(set)] (typename PropertyClass::ValueType v) { set(QVariant::fromValue(v)); }); + [set = std::move(set)] (const typename PropertyClass::ValueType &v) { set(QVariant::fromValue(v)); }); } Property *PropertyFactory::createProperty(const QString &name, diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 3dcdf86dec..4032c0800d 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -90,7 +90,6 @@ class Separator final : public Property {} DisplayMode displayMode() const override { return DisplayMode::Separator; } - QWidget *createEditor(QWidget */*parent*/) override { return nullptr; } }; @@ -104,8 +103,7 @@ class GroupProperty : public Property {} DisplayMode displayMode() const override { return DisplayMode::Header; } - - QWidget *createEditor(QWidget *parent) override; + QWidget *createEditor(QWidget */* parent */) override { return nullptr; } void addProperty(Property *property) { m_subProperties.append(property); } void addSeparator() { m_subProperties.append(new Separator(this)); } @@ -220,6 +218,11 @@ struct PointProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setSuffix(const QString &suffix) { m_suffix = suffix; } + +private: + QString m_suffix; }; struct PointFProperty : PropertyTemplate @@ -239,9 +242,11 @@ struct SizeProperty : PropertyTemplate QWidget *createEditor(QWidget *parent) override; void setMinimum(int minimum) { m_minimum = minimum; } + void setSuffix(const QString &suffix) { m_suffix = suffix; } private: int m_minimum; + QString m_suffix; }; struct SizeFProperty : PropertyTemplate From fed14c840753451ef7866ad869fad9ac84f0f8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 18 Sep 2024 11:37:46 +0200 Subject: [PATCH 27/78] Added support for custom enum types that are used as flags It creates a checkbox for each flag. They will need to be able to collapse in case there are lots of flags, but this works for now. --- src/tiled/propertieswidget.cpp | 14 +++++--- src/tiled/varianteditor.cpp | 63 ++++++++++++++++++++++++++------ src/tiled/varianteditor.h | 66 ++++++++++++++++++++-------------- 3 files changed, 100 insertions(+), 43 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 9a8f54a532..ef004baf99 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2126,18 +2126,22 @@ void PropertiesWidget::currentObjectChanged(Object *object) case PropertyType::PT_Class: // todo: class values break; - case PropertyType::PT_Enum: - auto enumType = static_cast(*propertyType); - // todo: support valuesAsFlags - property = new EnumProperty( + case PropertyType::PT_Enum: { + auto enumProperty = new BaseEnumProperty( name, [object, name] { return object->property(name).value().value.toInt(); }, [=](int value) { mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, propertyType->wrap(value))); }); - static_cast*>(property)->setEnumData(enumType.values); + + auto enumType = static_cast(*propertyType); + enumProperty->setEnumData(enumType.values); + enumProperty->setFlags(enumType.valuesAsFlags); + + property = enumProperty; break; } + } } } break; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index dff1069a4e..d4225fc4e7 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -609,34 +609,75 @@ QWidget *VariantEditor::createEditor(Property *property) } -QWidget *createEnumEditor(IntProperty *property, const EnumData &enumData, QWidget *parent) +QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { auto editor = new QComboBox(parent); // This allows the combo box to shrink horizontally. editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); - for (qsizetype i = 0; i < enumData.names.size(); ++i) { - auto value = enumData.values.isEmpty() ? i : enumData.values.value(i); - editor->addItem(enumData.icons[value], - enumData.names[i], + for (qsizetype i = 0; i < m_enumData.names.size(); ++i) { + auto value = m_enumData.values.value(i, i); + editor->addItem(m_enumData.icons[value], + m_enumData.names[i], value); } - auto syncEditor = [property, editor] { + auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setCurrentIndex(editor->findData(property->value())); + editor->setCurrentIndex(editor->findData(value())); }; syncEditor(); - QObject::connect(property, &Property::valueChanged, editor, syncEditor); - QObject::connect(editor, qOverload(&QComboBox::currentIndexChanged), property, - [editor, property] { - property->setValue(editor->currentData().toInt()); + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + QObject::connect(editor, qOverload(&QComboBox::currentIndexChanged), this, + [editor, this] { + setValue(editor->currentData().toInt()); }); return editor; } +QWidget *BaseEnumProperty::createFlagsEditor(QWidget *parent) +{ + auto editor = new QWidget(parent); + auto layout = new QVBoxLayout(editor); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + for (qsizetype i = 0; i < m_enumData.names.size(); ++i) { + auto checkBox = new QCheckBox(m_enumData.names[i], editor); + layout->addWidget(checkBox); + + QObject::connect(checkBox, &QCheckBox::toggled, this, [=](bool checked) { + const auto enumItemValue = m_enumData.values.value(i, 1 << i); + int flags = value(); + if (checked) + flags |= enumItemValue; + else + flags &= ~enumItemValue; + setValue(flags); + }); + } + + auto syncEditor = [=] { + for (int i = 0; i < layout->count(); ++i) { + auto checkBox = qobject_cast(layout->itemAt(i)->widget()); + if (checkBox) { + const auto enumItemValue = m_enumData.values.value(i, 1 << i); + + QSignalBlocker blocker(checkBox); + checkBox->setChecked((value() & enumItemValue) == enumItemValue); + } + } + }; + + syncEditor(); + + QObject::connect(this, &Property::valueChanged, editor, syncEditor); + + return editor; +} + Property *PropertyFactory::createQObjectProperty(QObject *qObject, const char *propertyName, const QString &displayName) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 4032c0800d..90ebab10c8 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -325,7 +325,7 @@ class PropertyFactory struct EnumData { - EnumData(const QStringList &names, + EnumData(const QStringList &names = {}, const QList &values = {}, const QMap &icons = {}) : names(names) @@ -344,44 +344,56 @@ EnumData enumData() return {{}}; } -QWidget *createEnumEditor(IntProperty *property, - const EnumData &enumData, - QWidget *parent); +/** + * A property that wraps an integer value and creates either a combo box or a + * list of checkboxes based on the given EnumData. + */ +class BaseEnumProperty : public IntProperty +{ + Q_OBJECT + +public: + using IntProperty::IntProperty; + + void setEnumData(const EnumData &enumData) { m_enumData = enumData; } + void setFlags(bool flags) { m_flags = flags; } + + QWidget *createEditor(QWidget *parent) override + { + return m_flags ? createFlagsEditor(parent) + : createEnumEditor(parent); + } + +protected: + QWidget *createFlagsEditor(QWidget *parent); + QWidget *createEnumEditor(QWidget *parent); + + EnumData m_enumData; + bool m_flags = false; +}; /** - * A property that wraps an enum value and creates a combo box based on the - * given EnumData. + * A property that wraps an enum value and automatically sets the EnumData + * based on the given type. */ template -class EnumProperty : public IntProperty +class EnumProperty : public BaseEnumProperty { public: EnumProperty(const QString &name, std::function get, std::function set, QObject *parent = nullptr) - : IntProperty(name, - [get] { - return static_cast(get()); - }, - set ? [set](const int &value){ set(static_cast(value)); } - : std::function(), - parent) - , m_enumData(enumData()) - {} - - void setEnumData(const EnumData &enumData) - { - m_enumData = enumData; - } - - QWidget *createEditor(QWidget *parent) override + : BaseEnumProperty(name, + [get] { + return static_cast(get()); + }, + set ? [set](const int &value){ set(static_cast(value)); } + : std::function(), + parent) { - return createEnumEditor(this, m_enumData, parent); + setEnumData(enumData()); } - -private: - EnumData m_enumData; }; From d40ab863fbcb03b90f19a963124129a3cda17748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 18 Sep 2024 17:38:54 +0200 Subject: [PATCH 28/78] Allow expanding the child properties for any GroupProperty Also when it isn't displayed as a header. Also introduced a "level" member of VariantEditor which is passed on to the PropertyLabel, which is used to indent expanded child properties. --- src/tiled/propertieswidget.cpp | 14 ++--- src/tiled/propertyeditorwidgets.cpp | 65 +++++++++++++++++------ src/tiled/propertyeditorwidgets.h | 41 ++++++++------- src/tiled/varianteditor.cpp | 82 +++++++++++++---------------- src/tiled/varianteditor.h | 7 ++- 5 files changed, 120 insertions(+), 89 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index ef004baf99..cbf5661643 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1275,7 +1275,7 @@ class TilesetProperties : public ObjectProperties push(new ChangeTilesetTransformationFlags(tilesetDocument(), value)); }); - // todo: sub-properties are not displayed yet and image file name doesn't update in the TilesetParametersEdit + // todo: image file name doesn't update in the TilesetParametersEdit mTilesetImageProperty = new TilesetImageProperty(document, this); mImageProperty = new UrlProperty( @@ -1302,6 +1302,12 @@ class TilesetProperties : public ObjectProperties mMarginProperty->setSuffix(tr(" px")); mTileSpacingProperty->setSuffix(tr(" px")); + mImageProperty->setEnabled(false); + mTransparentColorProperty->setEnabled(false); + mTileSizeProperty->setEnabled(false); + mMarginProperty->setEnabled(false); + mTileSpacingProperty->setEnabled(false); + mTilesetImageProperty->addProperty(mImageProperty); mTilesetImageProperty->addProperty(mTransparentColorProperty); mTilesetImageProperty->addProperty(mTileSizeProperty); @@ -1379,12 +1385,6 @@ class TilesetProperties : public ObjectProperties void updateEnabledState() { const bool collection = tileset()->isCollection(); - mTilesetImageProperty->setEnabled(!collection); - mImageProperty->setEnabled(!collection); - mTransparentColorProperty->setEnabled(!collection); - mTileSizeProperty->setEnabled(!collection); - mMarginProperty->setEnabled(!collection); - mTileSpacingProperty->setEnabled(!collection); mColumnCountProperty->setEnabled(collection); } diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 0d6af44cc2..24d2437486 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -491,38 +491,73 @@ void ElidingLabel::paintEvent(QPaintEvent *) } -HeaderWidget::HeaderWidget(const QString &text, QWidget *parent) - : ElidingLabel(text, parent) +PropertyLabel::PropertyLabel(int level, QWidget *parent) + : ElidingLabel(parent) { - setBackgroundRole(QPalette::Dark); - setForegroundRole(QPalette::BrightText); - setAutoFillBackground(true); + setMinimumWidth(Utils::dpiScaled(50)); + setLevel(level); +} + +void PropertyLabel::setLevel(int level) +{ + m_level = level; - const int spacing = Utils::dpiScaled(4); + const int spacing = Utils::dpiScaled(3); const int branchIndicatorWidth = Utils::dpiScaled(14); - setContentsMargins(spacing + branchIndicatorWidth, + setContentsMargins(spacing + branchIndicatorWidth * std::max(m_level, 1), spacing, spacing, spacing); } -void HeaderWidget::mousePressEvent(QMouseEvent *event) +void PropertyLabel::setHeader(bool header) +{ + if (m_header == header) + return; + + m_header = header; + setBackgroundRole(header ? QPalette::Dark : QPalette::NoRole); + setForegroundRole(header ? QPalette::BrightText : QPalette::NoRole); + setAutoFillBackground(header); +} + +void PropertyLabel::setExpandable(bool expandable) +{ + if (m_expandable == expandable) + return; + + m_expandable = expandable; + update(); +} + +void PropertyLabel::setExpanded(bool expanded) +{ + if (m_expanded == expanded) + return; + + m_expanded = expanded; + update(); + emit toggled(m_expanded); +} + +void PropertyLabel::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { - m_checked = !m_checked; - emit toggled(m_checked); + if (m_expandable && event->button() == Qt::LeftButton) { + setExpanded(!m_expanded); + return; } ElidingLabel::mousePressEvent(event); } -void HeaderWidget::paintEvent(QPaintEvent *event) +void PropertyLabel::paintEvent(QPaintEvent *event) { ElidingLabel::paintEvent(event); QStyleOption branchOption; branchOption.initFrom(this); branchOption.rect = QRect(0, 0, contentsMargins().left(), height()); - branchOption.state = QStyle::State_Children; - if (m_checked) + if (m_expandable) + branchOption.state |= QStyle::State_Children; + if (m_expanded) branchOption.state |= QStyle::State_Open; QStylePainter p(this); @@ -530,7 +565,7 @@ void HeaderWidget::paintEvent(QPaintEvent *event) } -QSize LineEditLabel::sizeHint() const +QSize PropertyLabel::sizeHint() const { auto hint = ElidingLabel::sizeHint(); hint.setHeight(m_lineEdit.sizeHint().height()); diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 493a95a02c..e5355c0f9e 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -274,40 +274,41 @@ class ElidingLabel : public QLabel }; /** - * A header widget that can be toggled. + * A property label widget, which can be a header or just be expandable. */ -class HeaderWidget : public ElidingLabel +class PropertyLabel : public ElidingLabel { Q_OBJECT public: - HeaderWidget(const QString &text, QWidget *parent = nullptr); + PropertyLabel(int level, QWidget *parent = nullptr); -signals: - void toggled(bool checked); + void setLevel(int level); -protected: - void mousePressEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *) override; + void setHeader(bool header); + bool isHeader() const { return m_header; } -private: - bool m_checked = true; -}; - -/** - * A label that matches its preferred height with that of a line edit. - */ -class LineEditLabel : public ElidingLabel -{ - Q_OBJECT + void setExpandable(bool expandable); + bool isExpandable() const { return m_expandable; } -public: - using ElidingLabel::ElidingLabel; + void setExpanded(bool expanded); + bool isExpanded() const { return m_expanded; } QSize sizeHint() const override; +signals: + void toggled(bool expanded); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *) override; + private: QLineEdit m_lineEdit; + int m_level = 0; + bool m_header = false; + bool m_expandable = false; + bool m_expanded = false; }; } // namespace Tiled diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index d4225fc4e7..aed7f6f81c 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -502,31 +502,30 @@ void VariantEditor::addSeparator() void VariantEditor::addProperty(Property *property) { - const int spacing = m_layout->spacing(); - const int branchIndicatorWidth = Utils::dpiScaled(14); + const auto displayMode = property->displayMode(); - switch (property->displayMode()) { - case Property::DisplayMode::Default: - case Property::DisplayMode::NoLabel: { + if (displayMode == Property::DisplayMode::Separator) { + addSeparator(); + return; + } + + auto label = new PropertyLabel(m_level, this); + + if (displayMode != Property::DisplayMode::NoLabel) { + label->setText(property->name()); + label->setToolTip(property->toolTip()); + label->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); + } + + if (displayMode == Property::DisplayMode::Header) { + label->setHeader(true); + m_layout->addWidget(label); + } else { auto propertyLayout = new QHBoxLayout; - propertyLayout->setContentsMargins(0, 0, spacing, 0); - - // Property label indentation, which shrinks when there is very little space - propertyLayout->addSpacerItem(new QSpacerItem(spacing + branchIndicatorWidth, 0, - QSizePolicy::Maximum)); - propertyLayout->setStretch(0, 1); - - if (property->displayMode() == Property::DisplayMode::Default) { - auto label = new LineEditLabel(property->name(), this); - label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - label->setToolTip(property->toolTip()); - label->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); - propertyLayout->addWidget(label, LabelStretch, Qt::AlignTop); - } else { - propertyLayout->addStretch(LabelStretch); - } + propertyLayout->setContentsMargins(0, 0, m_layout->spacing(), 0); + propertyLayout->addWidget(label, LabelStretch, Qt::AlignTop); if (auto editor = createEditor(property)) { editor->setToolTip(property->toolTip()); @@ -537,34 +536,26 @@ void VariantEditor::addProperty(Property *property) } m_layout->addLayout(propertyLayout); - - break; } - case Property::DisplayMode::Header: { - auto headerWidget = new HeaderWidget(property->name(), this); - m_layout->addWidget(headerWidget); - if (auto groupProperty = dynamic_cast(property)) { - auto editor = new VariantEditor(this); - for (auto property : groupProperty->subProperties()) - editor->addProperty(property); + if (auto groupProperty = dynamic_cast(property)) { + label->setExpandable(true); + label->setExpanded(label->isHeader()); - connect(headerWidget, &HeaderWidget::toggled, - editor, [this, editor](bool checked) { - editor->setVisible(checked); + auto editor = new VariantEditor(this); + editor->setLevel(m_level + 1); + editor->setVisible(label->isExpanded()); + for (auto property : groupProperty->subProperties()) + editor->addProperty(property); - // needed to avoid flickering when hiding the editor - layout()->activate(); - }); + connect(label, &PropertyLabel::toggled, editor, [=](bool expanded) { + editor->setVisible(expanded); - m_layout->addWidget(editor); - } + // needed to avoid flickering when hiding the editor + layout()->activate(); + }); - break; - } - case Property::DisplayMode::Separator: - addSeparator(); - break; + m_layout->addWidget(editor); } } @@ -600,6 +591,7 @@ void VariantEditor::addValue(const QVariant &value) QWidget *VariantEditor::createEditor(Property *property) { if (const auto editor = property->createEditor(this)) { + editor->setMinimumWidth(Utils::dpiScaled(70)); editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); return editor; } else { diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 90ebab10c8..43b0235b3f 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -32,7 +32,7 @@ class QVBoxLayout; namespace Tiled { -class HeaderWidget; +class PropertyLabel; /** * A property represents a named value that can create its own edit widget. @@ -133,7 +133,7 @@ class PropertyTemplate : public Property {} Type value() const { return m_get(); } - void setValue(const Type &value) { m_set(value); } + void setValue(const Type &value) { if (m_set) m_set(value); } private: std::function m_get; @@ -409,6 +409,8 @@ class VariantEditor : public QWidget void addProperty(Property *property); // void addValue(const QVariant &value); + void setLevel(int level) { m_level = level; } + private: QWidget *createEditor(Property *property); @@ -418,6 +420,7 @@ class VariantEditor : public QWidget }; QVBoxLayout *m_layout; + int m_level = 0; }; } // namespace Tiled From ba50ef99482e3881e6f20a4fc60a6308ac868188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 24 Sep 2024 17:08:19 +0200 Subject: [PATCH 29/78] Added support for custom class properties Including changing of nested values. However, several things remain to be done, including: * When the custom properties are refreshed, all properties (and their widgets) are re-created. There should be a way to update their value instead. * There is no way to see yet whether a property is set, nor can its value be reset. --- src/tiled/document.cpp | 1 + src/tiled/document.h | 1 + src/tiled/mapdocument.cpp | 12 +- src/tiled/propertieswidget.cpp | 273 ++++++++++++++++++++-------- src/tiled/propertieswidget.h | 2 + src/tiled/propertyeditorwidgets.cpp | 6 +- src/tiled/tilesetdocument.cpp | 3 + src/tiled/varianteditor.cpp | 150 +++++++++------ src/tiled/varianteditor.h | 63 +++++-- 9 files changed, 363 insertions(+), 148 deletions(-) diff --git a/src/tiled/document.cpp b/src/tiled/document.cpp index 7c546cd1e1..51c3fe7d4c 100644 --- a/src/tiled/document.cpp +++ b/src/tiled/document.cpp @@ -145,6 +145,7 @@ void Document::setCurrentObject(Object *object, Document *owningDocument) emit currentObjectSet(object); emit currentObjectChanged(object); + emit currentObjectsChanged(); } /** diff --git a/src/tiled/document.h b/src/tiled/document.h index a99b8049ac..62c2088bfb 100644 --- a/src/tiled/document.h +++ b/src/tiled/document.h @@ -139,6 +139,7 @@ class Document : public QObject, void currentObjectSet(Object *object); void currentObjectChanged(Object *object); + void currentObjectsChanged(); /** * Makes the Properties window visible and take focus. diff --git a/src/tiled/mapdocument.cpp b/src/tiled/mapdocument.cpp index 06ba3b6d1b..64da85f709 100644 --- a/src/tiled/mapdocument.cpp +++ b/src/tiled/mapdocument.cpp @@ -326,6 +326,9 @@ void MapDocument::setSelectedLayers(const QList &layers) mSelectedLayers = layers; emit selectedLayersChanged(); + + if (currentObject() && currentObject()->typeId() == Object::LayerType) + emit currentObjectsChanged(); } void MapDocument::switchCurrentLayer(Layer *layer) @@ -1277,8 +1280,10 @@ void MapDocument::setSelectedObjects(const QList &selectedObjects) // Make sure the current object is one of the selected ones if (!selectedObjects.isEmpty()) { if (currentObject() && currentObject()->typeId() == Object::MapObjectType) { - if (selectedObjects.contains(static_cast(currentObject()))) + if (selectedObjects.contains(static_cast(currentObject()))) { + emit currentObjectsChanged(); return; + } } setCurrentObject(selectedObjects.first()); @@ -1730,8 +1735,11 @@ void MapDocument::deselectObjects(const QList &objects) removedAboutToBeSelectedObjects += mAboutToBeSelectedObjects.removeAll(object); } - if (removedSelectedObjects > 0) + if (removedSelectedObjects > 0) { emit selectedObjectsChanged(); + if (mCurrentObject && mCurrentObject->typeId() == Object::MapObjectType) + emit currentObjectsChanged(); + } if (removedAboutToBeSelectedObjects > 0) emit aboutToBeSelectedObjectsChanged(mAboutToBeSelectedObjects); } diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index cbf5661643..e65ab82234 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -374,8 +375,48 @@ class TilesetImageProperty : public GroupProperty }; +class CustomProperties : public GroupProperty +{ + Q_OBJECT + +public: + CustomProperties(QObject *parent = nullptr) + : GroupProperty(tr("Custom Properties"), parent) + {} + + void setDocument(Document *document); + +private: + // todo: optimize + void propertyAdded(Object *, const QString &) { refresh(); } + void propertyRemoved(Object *, const QString &) { refresh(); } + void propertyChanged(Object *, const QString &) { + if (!mUpdating) + refresh(); + } + void propertiesChanged(Object *) { refresh(); } + + void refresh(); + + Property *createProperty(const QStringList &path, + std::function get, + std::function set); + + void createClassMembers(const QStringList &path, GroupProperty *groupProperty, + const ClassPropertyType &classType, + std::function get); + + void setPropertyValue(const QStringList &path, const QVariant &value); + + bool mUpdating = false; + Document *mDocument = nullptr; + Properties mProperties; +}; + + PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} + , mCustomProperties(new CustomProperties) , mScrollArea(new QScrollArea(this)) { auto scrollWidget = new QWidget(mScrollArea); @@ -453,6 +494,7 @@ void PropertiesWidget::setDocument(Document *document) mDocument = document; // mPropertyBrowser->setDocument(document); + mCustomProperties->setDocument(document); if (document) { connect(document, &Document::currentObjectChanged, @@ -496,12 +538,12 @@ static QStringList classNamesFor(const Object &object) } // todo: add support for changing multiple objects -class ClassProperty : public StringProperty +class ClassNameProperty : public StringProperty { Q_OBJECT public: - ClassProperty(Document *document, Object *object, QObject *parent = nullptr) + ClassNameProperty(Document *document, Object *object, QObject *parent = nullptr) : StringProperty(tr("Class"), [this] { return mObject->className(); }, [this] (const QString &value) { @@ -515,7 +557,7 @@ class ClassProperty : public StringProperty , mObject(object) { connect(mDocument, &Document::changed, - this, &ClassProperty::onChanged); + this, &ClassNameProperty::onChanged); } QWidget *createEditor(QWidget *parent) override @@ -606,7 +648,7 @@ class ObjectProperties : public QObject , mDocument(document) , mObject(object) { - mClassProperty = new ClassProperty(document, object, this); + mClassProperty = new ClassNameProperty(document, object, this); } virtual void populateEditor(VariantEditor *) @@ -2071,94 +2113,173 @@ void PropertiesWidget::currentObjectChanged(Object *object) } } - if (mPropertiesObject) + if (mPropertiesObject) { mPropertiesObject->populateEditor(mPropertyBrowser); + mPropertyBrowser->addProperty(mCustomProperties); + } + + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; + bool isTileset = object && object->isPartOfTileset(); + bool enabled = object && (!isTileset || editingTileset); - GroupProperty *customProperties = new GroupProperty(tr("Custom Properties")); + mPropertyBrowser->setEnabled(object); + mActionAddProperty->setEnabled(enabled); +} + + +void CustomProperties::setDocument(Document *document) +{ + if (mDocument == document) + return; + + if (mDocument) + mDocument->disconnect(this); - QMapIterator it(object ? object->properties() : Properties()); - PropertyFactory factory; + mDocument = document; + + if (document) { + connect(document, &Document::currentObjectsChanged, this, &CustomProperties::refresh); + + connect(document, &Document::propertyAdded, this, &CustomProperties::propertyAdded); + connect(document, &Document::propertyRemoved, this, &CustomProperties::propertyRemoved); + connect(document, &Document::propertyChanged, this, &CustomProperties::propertyChanged); + connect(document, &Document::propertiesChanged, this, &CustomProperties::propertiesChanged); + } + + refresh(); +} + +void CustomProperties::refresh() +{ + clear(); + + if (!mDocument || !mDocument->currentObject()) + return; + mProperties = mDocument->currentObject()->properties(); + + QMapIterator it(mProperties); while (it.hasNext()) { it.next(); + const QString &name = it.key(); + + auto get = [=] { return mProperties.value(name); }; + auto set = [=] (const QVariant &value) { + setPropertyValue({ name }, value); + }; + + if (auto property = createProperty({ name }, std::move(get), std::move(set))) + addProperty(property); + } +} - const auto &name = it.key(); - const auto &value = it.value(); - - Property *property = nullptr; - auto userType = value.userType(); - - switch (userType) { - case QMetaType::Bool: - case QMetaType::QColor: - case QMetaType::Double: - case QMetaType::Int: - case QMetaType::QString: { - auto get = [object, name] { return object->property(name); }; - auto set = [this, object, name] (const QVariant &value) { - mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, value)); +Property *CustomProperties::createProperty(const QStringList &path, + std::function get, + std::function set) +{ + const auto value = get(); + const auto type = value.userType(); + const auto &name = path.last(); + + switch (type) { + case QMetaType::Bool: + case QMetaType::QColor: + case QMetaType::Double: + case QMetaType::Int: + case QMetaType::QString: + return PropertyFactory::createProperty(name, std::move(get), std::move(set)); + + default: + if (type == filePathTypeId()) { + auto getUrl = [get = std::move(get)] { return get().value().url; }; + auto setUrl = [set = std::move(set)] (const QUrl &value) { + set(QVariant::fromValue(FilePath { value })); }; - property = factory.createProperty(name, std::move(get), std::move(set)); - break; + return new UrlProperty(name, std::move(getUrl), std::move(setUrl)); } - default: - if (userType == filePathTypeId()) { - auto get = [object, name] { return object->property(name).value().url; }; - auto set = [this, object, name](const QUrl &value) { - mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(FilePath { value }))); - }; - property = new UrlProperty(name, get, set); - } else if (userType == objectRefTypeId()) { - auto get = [this, object, name] { - return DisplayObjectRef(object->property(name).value(), - static_cast(mDocument)); - }; - auto set = [this, object, name](const DisplayObjectRef &value) { - mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, QVariant::fromValue(value.ref))); - }; - property = new ObjectRefProperty(name, get, set); - } else if (userType == propertyValueId()) { - auto propertyValue = value.value(); - if (auto propertyType = propertyValue.type()) { - switch (propertyType->type) { - case PropertyType::PT_Invalid: - break; - case PropertyType::PT_Class: - // todo: class values - break; - case PropertyType::PT_Enum: { - auto enumProperty = new BaseEnumProperty( - name, - [object, name] { return object->property(name).value().value.toInt(); }, - [=](int value) { - mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, propertyType->wrap(value))); - }); - - auto enumType = static_cast(*propertyType); - enumProperty->setEnumData(enumType.values); - enumProperty->setFlags(enumType.valuesAsFlags); - - property = enumProperty; - break; - } - } + + if (type == objectRefTypeId()) { + auto getObjectRef = [get = std::move(get), this] { + return DisplayObjectRef(get().value(), + static_cast(mDocument)); + }; + auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { + set(QVariant::fromValue(value.ref)); + }; + return new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); + } + + if (type == propertyValueId()) { + if (auto propertyType = value.value().type()) { + switch (propertyType->type) { + case PropertyType::PT_Invalid: + break; + case PropertyType::PT_Class: { + auto classType = static_cast(*propertyType); + + auto groupProperty = new GroupProperty(name); + groupProperty->setHeader(false); + + createClassMembers(path, groupProperty, classType, std::move(get)); + + return groupProperty; + } + case PropertyType::PT_Enum: { + auto enumProperty = new BaseEnumProperty( + name, + [get = std::move(get)] { return get().value().value.toInt(); }, + [set = std::move(set), propertyType](int value) { + set(propertyType->wrap(value)); + }); + + auto enumType = static_cast(*propertyType); + enumProperty->setEnumData(enumType.values); + enumProperty->setFlags(enumType.valuesAsFlags); + + return enumProperty; + } } } - break; } - - if (property) - customProperties->addProperty(property); } - mPropertyBrowser->addProperty(customProperties); + return nullptr; +} - bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; - bool isTileset = object && object->isPartOfTileset(); - bool enabled = object && (!isTileset || editingTileset); +void CustomProperties::createClassMembers(const QStringList &path, + GroupProperty *groupProperty, + const ClassPropertyType &classType, + std::function get) +{ + // Create a sub-property for each member + QMapIterator it(classType.members); + while (it.hasNext()) { + it.next(); + const QString &name = it.key(); - mPropertyBrowser->setEnabled(object); - mActionAddProperty->setEnabled(enabled); + auto childPath = path; + childPath.append(name); + + auto getMember = [=] { + auto def = classType.members.value(name); + return get().value().value.toMap().value(name, def); + }; + auto setMember = [=] (const QVariant &value) { + setPropertyValue(childPath, value); + }; + + if (auto childProperty = createProperty(childPath, std::move(getMember), std::move(setMember))) + groupProperty->addProperty(childProperty); + } +} + +void CustomProperties::setPropertyValue(const QStringList &path, const QVariant &value) +{ + const auto objects = mDocument->currentObjects(); + if (!objects.isEmpty()) { + QScopedValueRollback updating(mUpdating, true); + mDocument->undoStack()->push(new SetProperty(mDocument, objects, path, value)); + } } void PropertiesWidget::updateActions() diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index bda0cf672e..a38f10ddd4 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -28,6 +28,7 @@ namespace Tiled { class Object; +class CustomProperties; class Document; class ObjectProperties; class VariantEditor; @@ -78,6 +79,7 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; + CustomProperties *mCustomProperties = nullptr; QScrollArea *mScrollArea; VariantEditor *mPropertyBrowser; QAction *mActionAddProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 24d2437486..aac5955e95 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -552,9 +552,13 @@ void PropertyLabel::paintEvent(QPaintEvent *event) { ElidingLabel::paintEvent(event); + const int spacing = Utils::dpiScaled(3); + const int branchIndicatorWidth = Utils::dpiScaled(14); + QStyleOption branchOption; branchOption.initFrom(this); - branchOption.rect = QRect(0, 0, contentsMargins().left(), height()); + branchOption.rect = QRect(branchIndicatorWidth * std::max(m_level - 1, 0), 0, + branchIndicatorWidth + spacing, height()); if (m_expandable) branchOption.state |= QStyle::State_Children; if (m_expanded) diff --git a/src/tiled/tilesetdocument.cpp b/src/tiled/tilesetdocument.cpp index f378489189..5f74d7bd77 100644 --- a/src/tiled/tilesetdocument.cpp +++ b/src/tiled/tilesetdocument.cpp @@ -371,6 +371,9 @@ void TilesetDocument::setSelectedTiles(const QList &selectedTiles) { mSelectedTiles = selectedTiles; emit selectedTilesChanged(); + + if (currentObject() && currentObject()->typeId() == Object::TileType) + emit currentObjectsChanged(); } QList TilesetDocument::currentObjects() const diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index aed7f6f81c..580efa507f 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -486,79 +486,137 @@ VariantEditor::VariantEditor(QWidget *parent) // }); } +/** + * Removes all properties from the editor. The properties are not deleted. + */ void VariantEditor::clear() { - Utils::deleteAllFromLayout(m_layout); -} - -void VariantEditor::addSeparator() -{ - auto separator = new QFrame(this); - separator->setFrameShape(QFrame::HLine); - separator->setFrameShadow(QFrame::Plain); - separator->setForegroundRole(QPalette::Mid); - m_layout->addWidget(separator); + QHashIterator it(m_propertyWidgets); + while (it.hasNext()) { + it.next(); + auto &widgets = it.value(); + delete widgets.label; + delete widgets.editor; + delete widgets.children; + delete widgets.layout; + + it.key()->disconnect(this); + } + m_propertyWidgets.clear(); } +/** + * Adds the given property to the editor. Does not take ownership of the + * property. + */ void VariantEditor::addProperty(Property *property) { + auto &widgets = m_propertyWidgets[property]; const auto displayMode = property->displayMode(); + connect(property, &Property::destroyed, this, [this](QObject *object) { + removeProperty(static_cast(object)); + }); + if (displayMode == Property::DisplayMode::Separator) { - addSeparator(); + auto separator = new QFrame(this); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Plain); + separator->setForegroundRole(QPalette::Mid); + widgets.editor = separator; + m_layout->addWidget(widgets.editor); return; } - auto label = new PropertyLabel(m_level, this); + widgets.label = new PropertyLabel(m_level, this); if (displayMode != Property::DisplayMode::NoLabel) { - label->setText(property->name()); - label->setToolTip(property->toolTip()); - label->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); + widgets.label->setText(property->name()); + widgets.label->setToolTip(property->toolTip()); + widgets.label->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, widgets.label, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, widgets.label, &QWidget::setEnabled); } - if (displayMode == Property::DisplayMode::Header) { - label->setHeader(true); - m_layout->addWidget(label); - } else { + if (displayMode == Property::DisplayMode::Header) + widgets.label->setHeader(true); + + if (const auto editor = property->createEditor(this)) { auto propertyLayout = new QHBoxLayout; propertyLayout->setContentsMargins(0, 0, m_layout->spacing(), 0); - propertyLayout->addWidget(label, LabelStretch, Qt::AlignTop); - - if (auto editor = createEditor(property)) { - editor->setToolTip(property->toolTip()); - editor->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); - propertyLayout->addWidget(editor, WidgetStretch); - } - m_layout->addLayout(propertyLayout); + editor->setMinimumWidth(Utils::dpiScaled(70)); + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + editor->setToolTip(property->toolTip()); + editor->setEnabled(property->isEnabled()); + connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); + widgets.editor = editor; + + propertyLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); + propertyLayout->addWidget(widgets.editor, WidgetStretch); + + widgets.layout = propertyLayout; + m_layout->addLayout(widgets.layout); + } else { + m_layout->addWidget(widgets.label); } if (auto groupProperty = dynamic_cast(property)) { - label->setExpandable(true); - label->setExpanded(label->isHeader()); + widgets.label->setExpandable(true); + widgets.label->setExpanded(widgets.label->isHeader()); - auto editor = new VariantEditor(this); - editor->setLevel(m_level + 1); - editor->setVisible(label->isExpanded()); + auto children = new VariantEditor(this); + children->setLevel(m_level + 1); + children->setVisible(widgets.label->isExpanded()); for (auto property : groupProperty->subProperties()) - editor->addProperty(property); + children->addProperty(property); + + connect(groupProperty, &GroupProperty::propertyAdded, + children, &VariantEditor::addProperty); - connect(label, &PropertyLabel::toggled, editor, [=](bool expanded) { - editor->setVisible(expanded); + connect(widgets.label, &PropertyLabel::toggled, children, [=](bool expanded) { + children->setVisible(expanded); // needed to avoid flickering when hiding the editor layout()->activate(); }); - m_layout->addWidget(editor); + widgets.children = children; + m_layout->addWidget(widgets.children); } } +/** + * Removes the given property from the editor. The property is not deleted. + */ +void VariantEditor::removeProperty(Property *property) +{ + auto it = m_propertyWidgets.constFind(property); + Q_ASSERT(it != m_propertyWidgets.constEnd()); + + if (it != m_propertyWidgets.end()) { + auto &widgets = it.value(); + delete widgets.label; + delete widgets.editor; + delete widgets.children; + delete widgets.layout; + + m_propertyWidgets.erase(it); + } + + property->disconnect(this); +} + +void VariantEditor::setLevel(int level) +{ + m_level = level; + + setBackgroundRole((m_level % 2) ? QPalette::AlternateBase + : QPalette::Base); + setAutoFillBackground(m_level > 1); +} + #if 0 void VariantEditor::addValue(const QVariant &value) { @@ -588,18 +646,6 @@ void VariantEditor::addValue(const QVariant &value) } #endif -QWidget *VariantEditor::createEditor(Property *property) -{ - if (const auto editor = property->createEditor(this)) { - editor->setMinimumWidth(Utils::dpiScaled(70)); - editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - return editor; - } else { - qDebug() << "No editor for property" << property->name(); - } - return nullptr; -} - QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 43b0235b3f..ce9098114b 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include #include #include @@ -93,6 +94,10 @@ class Separator final : public Property QWidget *createEditor(QWidget */*parent*/) override { return nullptr; } }; +/** + * A property that can have sub-properties. The GroupProperty owns the sub- + * properties and will delete them when it is deleted. + */ class GroupProperty : public Property { Q_OBJECT @@ -102,15 +107,36 @@ class GroupProperty : public Property : Property(name, parent) {} - DisplayMode displayMode() const override { return DisplayMode::Header; } + ~GroupProperty() override { clear(); } + + DisplayMode displayMode() const override + { return m_header ? DisplayMode::Header : DisplayMode::Default; } + QWidget *createEditor(QWidget */* parent */) override { return nullptr; } - void addProperty(Property *property) { m_subProperties.append(property); } - void addSeparator() { m_subProperties.append(new Separator(this)); } + void setHeader(bool header) { m_header = header; } + + void clear() + { + qDeleteAll(m_subProperties); + m_subProperties.clear(); + } + + void addProperty(Property *property) + { + m_subProperties.append(property); + emit propertyAdded(property); + } + + void addSeparator() { addProperty(new Separator(this)); } const QList &subProperties() const { return m_subProperties; } +signals: + void propertyAdded(Property *property); + private: + bool m_header = true; QList m_subProperties; }; @@ -305,22 +331,20 @@ struct QtAlignmentProperty : PropertyTemplate class PropertyFactory { public: - PropertyFactory() = default; - /** * Creates a property that wraps a QObject property. */ - Property *createQObjectProperty(QObject *qObject, - const char *propertyName, - const QString &displayName = {}); + static Property *createQObjectProperty(QObject *qObject, + const char *propertyName, + const QString &displayName = {}); /** * Creates a property with the given name and get/set functions. The * value type determines the kind of property that will be created. */ - Property *createProperty(const QString &name, - std::function get, - std::function set); + static Property *createProperty(const QString &name, + std::function get, + std::function set); }; struct EnumData @@ -405,21 +429,26 @@ class VariantEditor : public QWidget VariantEditor(QWidget *parent = nullptr); void clear(); - void addSeparator(); void addProperty(Property *property); + void removeProperty(Property *property); // void addValue(const QVariant &value); - void setLevel(int level) { m_level = level; } + void setLevel(int level); private: - QWidget *createEditor(Property *property); + static constexpr int LabelStretch = 4; + static constexpr int WidgetStretch = 6; - enum Column { - LabelStretch = 4, - WidgetStretch = 6, + struct PropertyWidgets + { + PropertyLabel *label = nullptr; + QWidget *editor = nullptr; + QWidget *children = nullptr; + QLayout *layout = nullptr; }; QVBoxLayout *m_layout; + QHash m_propertyWidgets; int m_level = 0; }; From 382e4fe379d188b299cf2d12145dad2c750ba834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 26 Sep 2024 18:02:32 +0200 Subject: [PATCH 30/78] Show inherited class name as placeholder text For map objects, which can inherit their class from their tile or their template. --- src/tiled/propertieswidget.cpp | 21 +++++++++++++++++++-- src/tiled/textpropertyedit.h | 2 ++ src/tiled/varianteditor.cpp | 15 +++++++++++++++ src/tiled/varianteditor.h | 16 ++++++++++++++-- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index e65ab82234..2b6d695ed1 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -556,6 +556,8 @@ class ClassNameProperty : public StringProperty , mDocument(document) , mObject(object) { + updatePlaceholderText(); + connect(mDocument, &Document::changed, this, &ClassNameProperty::onChanged); } @@ -564,6 +566,7 @@ class ClassNameProperty : public StringProperty { auto editor = new QComboBox(parent); editor->setEditable(true); + editor->lineEdit()->setPlaceholderText(placeholderText()); editor->addItems(classNamesFor(*mObject)); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); @@ -571,11 +574,15 @@ class ClassNameProperty : public StringProperty }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); + connect(this, &StringProperty::placeholderTextChanged, + editor->lineEdit(), &QLineEdit::setPlaceholderText); connect(editor, &QComboBox::currentTextChanged, this, &StringProperty::setValue); connect(Preferences::instance(), &Preferences::propertyTypesChanged, - editor, [this,editor] { + editor, [=] { + const QSignalBlocker blocker(editor); editor->clear(); editor->addItems(classNamesFor(*mObject)); + syncEditor(); }); return editor; } @@ -590,8 +597,18 @@ class ClassNameProperty : public StringProperty if (!objectsEvent.objects.contains(mObject)) return; - if (objectsEvent.properties & ObjectsChangeEvent::ClassProperty) + if (objectsEvent.properties & ObjectsChangeEvent::ClassProperty) { + updatePlaceholderText(); emit valueChanged(); + } + } + + void updatePlaceholderText() + { + if (mObject->typeId() == Object::MapObjectType && mObject->className().isEmpty()) + setPlaceholderText(static_cast(mObject)->effectiveClassName()); + else + setPlaceholderText(QString()); } Document *mDocument; diff --git a/src/tiled/textpropertyedit.h b/src/tiled/textpropertyedit.h index 4ef2c7360f..a509ab38fc 100644 --- a/src/tiled/textpropertyedit.h +++ b/src/tiled/textpropertyedit.h @@ -45,6 +45,8 @@ class TextPropertyEdit : public QWidget QString text() const; + QLineEdit *lineEdit() const { return mLineEdit; } + public slots: void setText(const QString &text); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 580efa507f..d09e163f25 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -57,15 +57,26 @@ void Property::setEnabled(bool enabled) } } +void StringProperty::setPlaceholderText(const QString &placeholderText) +{ + if (m_placeholderText != placeholderText) { + m_placeholderText = placeholderText; + emit placeholderTextChanged(placeholderText); + } +} + QWidget *StringProperty::createEditor(QWidget *parent) { auto editor = new QLineEdit(parent); + editor->setPlaceholderText(m_placeholderText); + auto syncEditor = [=] { editor->setText(value()); }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); + connect(this, &StringProperty::placeholderTextChanged, editor, &QLineEdit::setPlaceholderText); connect(editor, &QLineEdit::textEdited, this, &StringProperty::setValue); return editor; @@ -74,6 +85,8 @@ QWidget *StringProperty::createEditor(QWidget *parent) QWidget *MultilineStringProperty::createEditor(QWidget *parent) { auto editor = new TextPropertyEdit(parent); + editor->lineEdit()->setPlaceholderText(placeholderText()); + auto syncEditor = [=] { const QSignalBlocker blocker(editor); editor->setText(value()); @@ -81,6 +94,8 @@ QWidget *MultilineStringProperty::createEditor(QWidget *parent) syncEditor(); connect(this, &StringProperty::valueChanged, editor, syncEditor); + connect(this, &StringProperty::placeholderTextChanged, + editor->lineEdit(), &QLineEdit::setPlaceholderText); connect(editor, &TextPropertyEdit::textChanged, this, &StringProperty::setValue); return editor; diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index ce9098114b..a4e72d4130 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -168,13 +168,25 @@ class PropertyTemplate : public Property struct StringProperty : PropertyTemplate { + Q_OBJECT + +public: using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setPlaceholderText(const QString &placeholderText); + const QString &placeholderText() const { return m_placeholderText; } + +signals: + void placeholderTextChanged(const QString &placeholderText); + +private: + QString m_placeholderText; }; -struct MultilineStringProperty : PropertyTemplate +struct MultilineStringProperty : StringProperty { - using PropertyTemplate::PropertyTemplate; + using StringProperty::StringProperty; QWidget *createEditor(QWidget *parent) override; }; From 74666e1daf5a1af6683eb3a74065f8639c4a3d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 27 Sep 2024 15:56:35 +0200 Subject: [PATCH 31/78] Implemented Remove and Reset buttons for custom properties * Top-level custom properties can now be removed. * Class members can be reset. A "modified" state was added to Property, which is updated as necessary and indicated by using bold font for the name. Further tweaks to the spacing between property widgets and draw line to separate property group headers. A generic VariantMapProperty was split off from the CustomProperties class, since the former can also be a base for editing class members. The ColorButton widget now replaces its icon with "Unset" text when an invalid color is set. When changing the "Class" it now affects all selected objects. Fixed layout flicker when collapsing nested class members. --- src/tiled/colorbutton.cpp | 3 +- src/tiled/propertieswidget.cpp | 379 +++++++++++++++++++--------- src/tiled/propertyeditorwidgets.cpp | 15 ++ src/tiled/propertyeditorwidgets.h | 2 + src/tiled/utils.cpp | 3 + src/tiled/varianteditor.cpp | 199 ++++++++------- src/tiled/varianteditor.h | 64 +++-- 7 files changed, 430 insertions(+), 235 deletions(-) diff --git a/src/tiled/colorbutton.cpp b/src/tiled/colorbutton.cpp index 33f87c5406..25c1d3751e 100644 --- a/src/tiled/colorbutton.cpp +++ b/src/tiled/colorbutton.cpp @@ -40,7 +40,7 @@ ColorButton::ColorButton(QWidget *parent) void ColorButton::setColor(const QColor &color) { - if (mColor == color || !color.isValid()) + if (mColor == color) return; mColor = color; @@ -77,6 +77,7 @@ void ColorButton::updateIcon() { // todo: fix gray icon in disabled state (consider using opacity, and not using an icon at all) setIcon(Utils::colorIcon(mColor, iconSize())); + setText(mColor.isValid() ? QString() : tr("Unset")); } #include "moc_colorbutton.cpp" diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 2b6d695ed1..ba11667291 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -374,43 +374,126 @@ class TilesetImageProperty : public GroupProperty TilesetDocument *mTilesetDocument; }; +static bool propertyValueAffected(Object *currentObject, + Object *changedObject, + const QString &propertyName) +{ + if (currentObject == changedObject) + return true; + + // Changed property may be inherited + if (currentObject && currentObject->typeId() == Object::MapObjectType && changedObject->typeId() == Object::TileType) { + auto tile = static_cast(currentObject)->cell().tile(); + if (tile == changedObject && !currentObject->hasProperty(propertyName)) + return true; + } + + return false; +} + +static bool objectPropertiesRelevant(Document *document, Object *object) +{ + auto currentObject = document->currentObject(); + if (!currentObject) + return false; -class CustomProperties : public GroupProperty + if (currentObject == object) + return true; + + if (currentObject->typeId() == Object::MapObjectType) + if (static_cast(currentObject)->cell().tile() == object) + return true; + + if (document->currentObjects().contains(object)) + return true; + + return false; +} + + +class VariantMapProperty : public GroupProperty { Q_OBJECT public: - CustomProperties(QObject *parent = nullptr) - : GroupProperty(tr("Custom Properties"), parent) - {} + VariantMapProperty(const QString &name, QObject *parent = nullptr) + : GroupProperty(name, parent) + { + } - void setDocument(Document *document); + void setValue(const QVariantMap &value); + const QVariantMap &value() const { return mValue; } -private: - // todo: optimize - void propertyAdded(Object *, const QString &) { refresh(); } - void propertyRemoved(Object *, const QString &) { refresh(); } - void propertyChanged(Object *, const QString &) { - if (!mUpdating) - refresh(); - } - void propertiesChanged(Object *) { refresh(); } +signals: + void memberValueChanged(const QStringList &path, const QVariant &value); - void refresh(); +protected: + Document *mDocument = nullptr; +private: Property *createProperty(const QStringList &path, std::function get, std::function set); - void createClassMembers(const QStringList &path, GroupProperty *groupProperty, + void createClassMembers(const QStringList &path, + GroupProperty *groupProperty, const ClassPropertyType &classType, std::function get); + void updateModifiedRecursively(Property *property, const QVariant &value); + void emitValueChangedRecursively(Property *property); + + QVariantMap mValue; + QHash mPropertyMap; +}; + + +class CustomProperties : public VariantMapProperty +{ + Q_OBJECT + +public: + CustomProperties(QObject *parent = nullptr) + : VariantMapProperty(tr("Custom Properties"), parent) + { + connect(this, &VariantMapProperty::memberValueChanged, + this, &CustomProperties::setPropertyValue); + } + + void setDocument(Document *document); + +private: + // todo: optimize further + void propertyAdded(Object *object, const QString &) { + if (!objectPropertiesRelevant(mDocument, object)) + return; + refresh(); + } + void propertyRemoved(Object *object, const QString &) { + if (mUpdating) + return; + if (!objectPropertiesRelevant(mDocument, object)) + return; + refresh(); + } + void propertyChanged(Object *object, const QString &name) { + if (mUpdating) + return; + if (!propertyValueAffected(mDocument->currentObject(), object, name)) + return; + refresh(); + } + void propertiesChanged(Object *object) { + if (!objectPropertiesRelevant(mDocument, object)) + return; + refresh(); + } + + void refresh(); + void setPropertyValue(const QStringList &path, const QVariant &value); bool mUpdating = false; - Document *mDocument = nullptr; - Properties mProperties; }; @@ -421,6 +504,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) { auto scrollWidget = new QWidget(mScrollArea); scrollWidget->setBackgroundRole(QPalette::AlternateBase); + scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); auto verticalLayout = new QVBoxLayout(scrollWidget); mPropertyBrowser = new VariantEditor(scrollWidget); @@ -537,7 +621,6 @@ static QStringList classNamesFor(const Object &object) return names; } -// todo: add support for changing multiple objects class ClassNameProperty : public StringProperty { Q_OBJECT @@ -549,7 +632,7 @@ class ClassNameProperty : public StringProperty [this] (const QString &value) { QUndoStack *undoStack = mDocument->undoStack(); undoStack->push(new ChangeClassName(mDocument, - { mObject }, + mDocument->currentObjects(), value)); }, parent) @@ -2144,129 +2227,117 @@ void PropertiesWidget::currentObjectChanged(Object *object) } -void CustomProperties::setDocument(Document *document) +void VariantMapProperty::setValue(const QVariantMap &value) { - if (mDocument == document) - return; - - if (mDocument) - mDocument->disconnect(this); - - mDocument = document; - - if (document) { - connect(document, &Document::currentObjectsChanged, this, &CustomProperties::refresh); - - connect(document, &Document::propertyAdded, this, &CustomProperties::propertyAdded); - connect(document, &Document::propertyRemoved, this, &CustomProperties::propertyRemoved); - connect(document, &Document::propertyChanged, this, &CustomProperties::propertyChanged); - connect(document, &Document::propertiesChanged, this, &CustomProperties::propertiesChanged); - } - - refresh(); -} + mValue = value; -void CustomProperties::refresh() -{ clear(); - if (!mDocument || !mDocument->currentObject()) - return; - - mProperties = mDocument->currentObject()->properties(); - - QMapIterator it(mProperties); + QMapIterator it(mValue); while (it.hasNext()) { it.next(); const QString &name = it.key(); - auto get = [=] { return mProperties.value(name); }; + auto get = [=] { + return mValue.value(name); + }; auto set = [=] (const QVariant &value) { - setPropertyValue({ name }, value); + mValue.insert(name, value); + emit memberValueChanged({ name }, value); + emit valueChanged(); }; - if (auto property = createProperty({ name }, std::move(get), std::move(set))) + if (auto property = createProperty({ name }, std::move(get), std::move(set))) { + property->setActions(Property::Remove); + + updateModifiedRecursively(property, it.value()); + addProperty(property); + mPropertyMap.insert(name, property); + + connect(property, &Property::removeRequested, this, [=] { + mValue.remove(name); + + if (auto property = mPropertyMap.take(name)) + deleteProperty(property); + + emit memberValueChanged({ name }, QVariant()); + emit valueChanged(); + }); + } } + + emit valueChanged(); } -Property *CustomProperties::createProperty(const QStringList &path, - std::function get, - std::function set) +Property *VariantMapProperty::createProperty(const QStringList &path, + std::function get, + std::function set) { const auto value = get(); const auto type = value.userType(); const auto &name = path.last(); - switch (type) { - case QMetaType::Bool: - case QMetaType::QColor: - case QMetaType::Double: - case QMetaType::Int: - case QMetaType::QString: - return PropertyFactory::createProperty(name, std::move(get), std::move(set)); - - default: - if (type == filePathTypeId()) { - auto getUrl = [get = std::move(get)] { return get().value().url; }; - auto setUrl = [set = std::move(set)] (const QUrl &value) { - set(QVariant::fromValue(FilePath { value })); - }; - return new UrlProperty(name, std::move(getUrl), std::move(setUrl)); - } + if (type == filePathTypeId()) { + auto getUrl = [get = std::move(get)] { return get().value().url; }; + auto setUrl = [set = std::move(set)] (const QUrl &value) { + set(QVariant::fromValue(FilePath { value })); + }; + return new UrlProperty(name, std::move(getUrl), std::move(setUrl)); + } - if (type == objectRefTypeId()) { - auto getObjectRef = [get = std::move(get), this] { - return DisplayObjectRef(get().value(), - static_cast(mDocument)); - }; - auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { - set(QVariant::fromValue(value.ref)); - }; - return new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); - } + if (type == objectRefTypeId()) { + auto getObjectRef = [get = std::move(get), this] { + return DisplayObjectRef(get().value(), + static_cast(mDocument)); + }; + auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { + set(QVariant::fromValue(value.ref)); + }; + return new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); + } - if (type == propertyValueId()) { - if (auto propertyType = value.value().type()) { - switch (propertyType->type) { - case PropertyType::PT_Invalid: - break; - case PropertyType::PT_Class: { - auto classType = static_cast(*propertyType); + if (type == propertyValueId()) { + const auto propertyValue = value.value(); + if (auto propertyType = propertyValue.type()) { + switch (propertyType->type) { + case PropertyType::PT_Invalid: + break; + case PropertyType::PT_Class: { + auto classType = static_cast(*propertyType); - auto groupProperty = new GroupProperty(name); - groupProperty->setHeader(false); + auto groupProperty = new GroupProperty(name); + groupProperty->setHeader(false); - createClassMembers(path, groupProperty, classType, std::move(get)); + createClassMembers(path, groupProperty, classType, std::move(get)); - return groupProperty; - } - case PropertyType::PT_Enum: { - auto enumProperty = new BaseEnumProperty( - name, - [get = std::move(get)] { return get().value().value.toInt(); }, - [set = std::move(set), propertyType](int value) { - set(propertyType->wrap(value)); - }); - - auto enumType = static_cast(*propertyType); - enumProperty->setEnumData(enumType.values); - enumProperty->setFlags(enumType.valuesAsFlags); - - return enumProperty; - } - } + return groupProperty; + } + case PropertyType::PT_Enum: { + auto enumProperty = new BaseEnumProperty( + name, + [get = std::move(get)] { return get().value().value.toInt(); }, + [set = std::move(set), propertyType](int value) { + set(propertyType->wrap(value)); + }); + + auto enumType = static_cast(*propertyType); + enumProperty->setEnumData(enumType.values); + enumProperty->setFlags(enumType.valuesAsFlags); + + return enumProperty; + } } } } - return nullptr; + return createVariantProperty(name, std::move(get), std::move(set)); } -void CustomProperties::createClassMembers(const QStringList &path, - GroupProperty *groupProperty, - const ClassPropertyType &classType, - std::function get) +void VariantMapProperty::createClassMembers(const QStringList &path, + GroupProperty *groupProperty, + const ClassPropertyType &classType, + std::function get) { // Create a sub-property for each member QMapIterator it(classType.members); @@ -2282,23 +2353,101 @@ void CustomProperties::createClassMembers(const QStringList &path, return get().value().value.toMap().value(name, def); }; auto setMember = [=] (const QVariant &value) { - setPropertyValue(childPath, value); + if (setPropertyMemberValue(mValue, childPath, value)) { + const auto &topLevelName = childPath.first(); + updateModifiedRecursively(mPropertyMap.value(topLevelName), + mValue.value(topLevelName)); + + emit memberValueChanged(childPath, value); + emit valueChanged(); + } }; - if (auto childProperty = createProperty(childPath, std::move(getMember), std::move(setMember))) + if (auto childProperty = createProperty(childPath, std::move(getMember), setMember)) { + childProperty->setActions(Property::Reset); groupProperty->addProperty(childProperty); + + connect(childProperty, &Property::resetRequested, this, [=] { + setMember(QVariant()); + emitValueChangedRecursively(childProperty); + }); + } } } +void VariantMapProperty::updateModifiedRecursively(Property *property, + const QVariant &value) +{ + auto groupProperty = dynamic_cast(property); + if (!groupProperty) + return; + + const QVariantMap classValue = value.value().value.toMap(); + + for (auto subProperty : groupProperty->subProperties()) { + const auto &name = subProperty->name(); + const bool isModified = classValue.contains(name); + + if (subProperty->isModified() != isModified || subProperty->isModified()) { + subProperty->setModified(isModified); + updateModifiedRecursively(subProperty, classValue.value(name)); + } + } +} + +void VariantMapProperty::emitValueChangedRecursively(Property *property) +{ + emit property->valueChanged(); + + if (auto groupProperty = dynamic_cast(property)) + for (auto subProperty : groupProperty->subProperties()) + emitValueChangedRecursively(subProperty); +} + + +void CustomProperties::setDocument(Document *document) +{ + if (mDocument == document) + return; + + if (mDocument) + mDocument->disconnect(this); + + mDocument = document; + + if (document) { + connect(document, &Document::currentObjectsChanged, this, &CustomProperties::refresh); + + connect(document, &Document::propertyAdded, this, &CustomProperties::propertyAdded); + connect(document, &Document::propertyRemoved, this, &CustomProperties::propertyRemoved); + connect(document, &Document::propertyChanged, this, &CustomProperties::propertyChanged); + connect(document, &Document::propertiesChanged, this, &CustomProperties::propertiesChanged); + } + + refresh(); +} + +void CustomProperties::refresh() +{ + if (mDocument && mDocument->currentObject()) + setValue(mDocument->currentObject()->properties()); + else + setValue({}); +} + void CustomProperties::setPropertyValue(const QStringList &path, const QVariant &value) { const auto objects = mDocument->currentObjects(); if (!objects.isEmpty()) { QScopedValueRollback updating(mUpdating, true); - mDocument->undoStack()->push(new SetProperty(mDocument, objects, path, value)); + if (path.size() > 1 || value.isValid()) + mDocument->undoStack()->push(new SetProperty(mDocument, objects, path, value)); + else + mDocument->undoStack()->push(new RemoveProperty(mDocument, objects, path.first())); } } + void PropertiesWidget::updateActions() { #if 0 diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index aac5955e95..284248c9a2 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -538,6 +538,13 @@ void PropertyLabel::setExpanded(bool expanded) emit toggled(m_expanded); } +void PropertyLabel::setModified(bool modified) +{ + auto f = font(); + f.setBold(modified); + setFont(f); +} + void PropertyLabel::mousePressEvent(QMouseEvent *event) { if (m_expandable && event->button() == Qt::LeftButton) { @@ -566,6 +573,14 @@ void PropertyLabel::paintEvent(QPaintEvent *event) QStylePainter p(this); p.drawPrimitive(QStyle::PE_IndicatorBranch, branchOption); + + if (m_header) { + const QColor color = static_cast(p.style()->styleHint(QStyle::SH_Table_GridLineColor, &branchOption)); + p.save(); + p.setPen(QPen(color)); + p.drawLine(0, height() - 1, width(), height() - 1); + p.restore(); + } } diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index e5355c0f9e..cf3330ccd1 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -294,6 +294,8 @@ class PropertyLabel : public ElidingLabel void setExpanded(bool expanded); bool isExpanded() const { return m_expanded; } + void setModified(bool modified); + QSize sizeHint() const override; signals: diff --git a/src/tiled/utils.cpp b/src/tiled/utils.cpp index ac7538ccc3..a33c256869 100644 --- a/src/tiled/utils.cpp +++ b/src/tiled/utils.cpp @@ -312,6 +312,9 @@ QIcon themeIcon(const QString &name) QIcon colorIcon(const QColor &color, QSize size) { + if (!color.isValid()) + return QIcon(); + QPixmap pixmap(size); pixmap.fill(color); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index d09e163f25..a73a3831e1 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -57,6 +57,14 @@ void Property::setEnabled(bool enabled) } } +void Property::setModified(bool modified) +{ + if (m_modified != modified) { + m_modified = modified; + emit modifiedChanged(modified); + } +} + void StringProperty::setPlaceholderText(const QString &placeholderText) { if (m_placeholderText != placeholderText) { @@ -183,10 +191,17 @@ QWidget *BoolProperty::createEditor(QWidget *parent) const QSignalBlocker blocker(editor); bool checked = value(); editor->setChecked(checked); + + // Reflect modified state on the checkbox, since we're not showing the + // property label. + auto font = editor->font(); + font.setBold(isModified()); + editor->setFont(font); }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); + connect(this, &Property::modifiedChanged, editor, syncEditor); connect(editor, &QCheckBox::toggled, this, &BoolProperty::setValue); return editor; @@ -480,25 +495,7 @@ VariantEditor::VariantEditor(QWidget *parent) m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(QMargins()); - m_layout->setSpacing(Utils::dpiScaled(4)); - - // setValue(QVariantMap { - // { QStringLiteral("Name"), QVariant(QLatin1String("Hello")) }, - // { QStringLiteral("Position"), QVariant(QPoint(15, 50)) }, - // { QStringLiteral("Size"), QVariant(QSize(35, 400)) }, - // { QStringLiteral("Rectangle"), QVariant(QRectF(15, 50, 35, 400)) }, - // { QStringLiteral("Margin"), QVariant(10) }, - // { QStringLiteral("Opacity"), QVariant(0.5) }, - // { QStringLiteral("Visible"), true }, - // { QStringLiteral("Object Alignment"), QVariant::fromValue(TopLeft) }, - // }); - - - // setValue(QVariantList { - // QVariant(QLatin1String("Hello")), - // QVariant(10), - // QVariant(3.14) - // }); + m_layout->setSpacing(0); } /** @@ -510,10 +507,9 @@ void VariantEditor::clear() while (it.hasNext()) { it.next(); auto &widgets = it.value(); - delete widgets.label; - delete widgets.editor; - delete widgets.children; + Utils::deleteAllFromLayout(widgets.layout); delete widgets.layout; + delete widgets.children; it.key()->disconnect(this); } @@ -533,68 +529,107 @@ void VariantEditor::addProperty(Property *property) removeProperty(static_cast(object)); }); + const auto halfSpacing = Utils::dpiScaled(2); + + widgets.layout = new QHBoxLayout; + widgets.layout->setSpacing(halfSpacing * 2); + if (displayMode == Property::DisplayMode::Separator) { auto separator = new QFrame(this); + widgets.layout->setContentsMargins(0, halfSpacing, 0, halfSpacing); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); - widgets.editor = separator; - m_layout->addWidget(widgets.editor); + widgets.layout->addWidget(separator); + m_layout->addLayout(widgets.layout); return; } - widgets.label = new PropertyLabel(m_level, this); + auto label = new PropertyLabel(m_level, this); if (displayMode != Property::DisplayMode::NoLabel) { - widgets.label->setText(property->name()); - widgets.label->setToolTip(property->toolTip()); - widgets.label->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, widgets.label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, widgets.label, &QWidget::setEnabled); + label->setText(property->name()); + label->setToolTip(property->toolTip()); + label->setEnabled(property->isEnabled()); + label->setModified(property->isModified()); + connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); + connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); + connect(property, &Property::modifiedChanged, label, &PropertyLabel::setModified); } if (displayMode == Property::DisplayMode::Header) - widgets.label->setHeader(true); + label->setHeader(true); + else + widgets.layout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); + + widgets.layout->addWidget(label, LabelStretch, Qt::AlignTop); - if (const auto editor = property->createEditor(this)) { - auto propertyLayout = new QHBoxLayout; - propertyLayout->setContentsMargins(0, 0, m_layout->spacing(), 0); + QHBoxLayout *editorLayout = widgets.layout; + const auto editor = property->createEditor(this); - editor->setMinimumWidth(Utils::dpiScaled(70)); + if (editor && property->actions()) { + editorLayout = new QHBoxLayout; + widgets.layout->addLayout(editorLayout, WidgetStretch); + } + + if (editor) { editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); editor->setToolTip(property->toolTip()); editor->setEnabled(property->isEnabled()); connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); - widgets.editor = editor; - propertyLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); - propertyLayout->addWidget(widgets.editor, WidgetStretch); + editorLayout->addWidget(editor, WidgetStretch, Qt::AlignTop); + } - widgets.layout = propertyLayout; - m_layout->addLayout(widgets.layout); - } else { - m_layout->addWidget(widgets.label); + if (property->actions()) { + if (property->actions() & Property::Reset) { + auto resetButton = new QToolButton; + resetButton->setToolTip(tr("Reset")); + resetButton->setAutoRaise(true); + resetButton->setEnabled(property->isModified()); + Utils::setThemeIcon(resetButton, "edit-clear"); + editorLayout->addWidget(resetButton, 0, Qt::AlignTop); + connect(resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); + connect(property, &Property::modifiedChanged, resetButton, &QWidget::setEnabled); + } + + if (property->actions() & Property::Remove) { + auto removeButton = new QToolButton; + removeButton->setToolTip(tr("Remove")); + removeButton->setAutoRaise(true); + Utils::setThemeIcon(removeButton, "remove"); + editorLayout->addWidget(removeButton, 0, Qt::AlignTop); + connect(removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); + } } + m_layout->addLayout(widgets.layout); + if (auto groupProperty = dynamic_cast(property)) { - widgets.label->setExpandable(true); - widgets.label->setExpanded(widgets.label->isHeader()); + label->setExpandable(true); + label->setExpanded(label->isHeader()); auto children = new VariantEditor(this); + if (label->isHeader()) + children->setContentsMargins(0, halfSpacing, 0, halfSpacing); children->setLevel(m_level + 1); - children->setVisible(widgets.label->isExpanded()); + children->setVisible(label->isExpanded()); for (auto property : groupProperty->subProperties()) children->addProperty(property); connect(groupProperty, &GroupProperty::propertyAdded, children, &VariantEditor::addProperty); - connect(widgets.label, &PropertyLabel::toggled, children, [=](bool expanded) { + connect(label, &PropertyLabel::toggled, children, [=](bool expanded) { children->setVisible(expanded); // needed to avoid flickering when hiding the editor - layout()->activate(); + QWidget *widget = this; + while (widget && widget->layout()) { + widget->layout()->activate(); + widget = widget->parentWidget(); + } }); widgets.children = children; @@ -612,10 +647,9 @@ void VariantEditor::removeProperty(Property *property) if (it != m_propertyWidgets.end()) { auto &widgets = it.value(); - delete widgets.label; - delete widgets.editor; - delete widgets.children; + Utils::deleteAllFromLayout(widgets.layout); delete widgets.layout; + delete widgets.children; m_propertyWidgets.erase(it); } @@ -632,35 +666,6 @@ void VariantEditor::setLevel(int level) setAutoFillBackground(m_level > 1); } -#if 0 -void VariantEditor::addValue(const QVariant &value) -{ - const int type = value.userType(); - switch (type) { - case QMetaType::QVariantList: { - const auto list = value.toList(); - for (const auto &item : list) - addValue(item); - break; - } - case QMetaType::QVariantMap: { - const auto map = value.toMap(); - for (auto it = map.constBegin(); it != map.constEnd(); ++it) - addValue(it.key(), it.value()); - break; - } - default: { - if (auto editor = createEditor(value)) - m_gridLayout->addWidget(editor, m_rowIndex, LabelColumn, 1, 3); - else - qDebug() << "No editor factory for type" << type; - - ++m_rowIndex; - } - } -} -#endif - QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { @@ -731,9 +736,12 @@ QWidget *BaseEnumProperty::createFlagsEditor(QWidget *parent) return editor; } -Property *PropertyFactory::createQObjectProperty(QObject *qObject, - const char *propertyName, - const QString &displayName) +/** + * Creates a property that wraps a QObject property. + */ +Property *createQObjectProperty(QObject *qObject, + const char *propertyName, + const QString &displayName) { auto metaObject = qObject->metaObject(); auto propertyIndex = metaObject->indexOfProperty(propertyName); @@ -741,14 +749,15 @@ Property *PropertyFactory::createQObjectProperty(QObject *qObject, return nullptr; auto metaProperty = metaObject->property(propertyIndex); - auto property = createProperty(displayName.isEmpty() ? QString::fromUtf8(propertyName) - : displayName, - [=] { - return metaProperty.read(qObject); - }, - [=] (const QVariant &value) { - metaProperty.write(qObject, value); - }); + auto property = createVariantProperty( + displayName.isEmpty() ? QString::fromUtf8(propertyName) + : displayName, + [=] { + return metaProperty.read(qObject); + }, + [=] (const QVariant &value) { + metaProperty.write(qObject, value); + }); // If the property has a notify signal, forward it to valueChanged auto notify = metaProperty.notifySignal(); @@ -776,9 +785,13 @@ Property *createTypedProperty(const QString &name, [set = std::move(set)] (const typename PropertyClass::ValueType &v) { set(QVariant::fromValue(v)); }); } -Property *PropertyFactory::createProperty(const QString &name, - std::function get, - std::function set) +/** + * Creates a property with the given name and get/set functions. The + * value type determines the kind of property that will be created. + */ +Property *createVariantProperty(const QString &name, + std::function get, + std::function set) { const auto type = get().userType(); switch (type) { diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index a4e72d4130..c4dc66a928 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -29,6 +29,7 @@ #include #include +class QHBoxLayout; class QVBoxLayout; namespace Tiled { @@ -44,6 +45,7 @@ class Property : public QObject Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged) public: enum class DisplayMode { @@ -53,6 +55,12 @@ class Property : public QObject Separator }; + enum Action { + Reset = 0x01, + Remove = 0x02, + }; + Q_DECLARE_FLAGS(Actions, Action) + Property(const QString &name, QObject *parent = nullptr) : QObject(parent) , m_name(name) @@ -66,6 +74,12 @@ class Property : public QObject bool isEnabled() const { return m_enabled; } void setEnabled(bool enabled); + bool isModified() const { return m_modified; } + void setModified(bool modified); + + Actions actions() const { return m_actions; } + void setActions(Actions actions) { m_actions = actions; } + virtual DisplayMode displayMode() const { return DisplayMode::Default; } virtual QWidget *createEditor(QWidget *parent) = 0; @@ -74,11 +88,19 @@ class Property : public QObject void toolTipChanged(const QString &toolTip); void valueChanged(); void enabledChanged(bool enabled); + void modifiedChanged(bool modified); + + void resetRequested(); + void removeRequested(); private: + friend class GroupProperty; + QString m_name; QString m_toolTip; bool m_enabled = true; + bool m_modified = false; + Actions m_actions; }; class Separator final : public Property @@ -128,6 +150,12 @@ class GroupProperty : public Property emit propertyAdded(property); } + void deleteProperty(Property *property) + { + m_subProperties.removeOne(property); + delete property; + } + void addSeparator() { addProperty(new Separator(this)); } const QList &subProperties() const { return m_subProperties; } @@ -336,28 +364,13 @@ struct QtAlignmentProperty : PropertyTemplate }; -/** - * A property factory that instantiates the appropriate property type based on - * the type of the property value. - */ -class PropertyFactory -{ -public: - /** - * Creates a property that wraps a QObject property. - */ - static Property *createQObjectProperty(QObject *qObject, - const char *propertyName, - const QString &displayName = {}); - - /** - * Creates a property with the given name and get/set functions. The - * value type determines the kind of property that will be created. - */ - static Property *createProperty(const QString &name, - std::function get, - std::function set); -}; +Property *createQObjectProperty(QObject *qObject, + const char *propertyName, + const QString &displayName = {}); + +Property *createVariantProperty(const QString &name, + std::function get, + std::function set); struct EnumData { @@ -443,7 +456,6 @@ class VariantEditor : public QWidget void clear(); void addProperty(Property *property); void removeProperty(Property *property); - // void addValue(const QVariant &value); void setLevel(int level); @@ -453,10 +465,8 @@ class VariantEditor : public QWidget struct PropertyWidgets { - PropertyLabel *label = nullptr; - QWidget *editor = nullptr; + QHBoxLayout *layout = nullptr; QWidget *children = nullptr; - QLayout *layout = nullptr; }; QVBoxLayout *m_layout; @@ -465,3 +475,5 @@ class VariantEditor : public QWidget }; } // namespace Tiled + +Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::Property::Actions) From c99a1e77de2b41bad638b67272562f08e8289118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 1 Oct 2024 09:03:29 +0200 Subject: [PATCH 32/78] Added ability to add (override) an inherited property In the previous property editor, overriding happened automatically as soon as you changed a value. Now that the edit widgets are always visible, I've instead opted to show a disabled widget for inherited properties along with a '+' button for adding (overriding) that property. I think this helps unintensionally setting a value, and the disabled state. Currently the VariantMapProperty does not yet handle a possible change of property type when a removed property has a different type than the inherited property. --- src/libtiled/object.cpp | 41 ++++--- src/libtiled/object.h | 3 + src/tiled/changeproperties.cpp | 6 +- src/tiled/colorbutton.cpp | 6 +- src/tiled/colorbutton.h | 5 + src/tiled/propertieswidget.cpp | 91 ++++++++++++--- src/tiled/propertyeditorwidgets.cpp | 17 ++- src/tiled/propertyeditorwidgets.h | 2 +- src/tiled/scriptmodule.cpp | 4 +- src/tiled/varianteditor.cpp | 168 +++++++++++++++++----------- src/tiled/varianteditor.h | 28 ++++- 11 files changed, 260 insertions(+), 111 deletions(-) diff --git a/src/libtiled/object.cpp b/src/libtiled/object.cpp index e8d1acc9f2..357ed62d06 100644 --- a/src/libtiled/object.cpp +++ b/src/libtiled/object.cpp @@ -40,6 +40,17 @@ Object::~Object() delete mEditable; } +const ClassPropertyType *Object::classType() const +{ + QString objectClassName = className(); + if (objectClassName.isEmpty() && typeId() == Object::MapObjectType) { + auto mapObject = static_cast(this); + objectClassName = mapObject->effectiveClassName(); + } + + return propertyTypes().findClassFor(objectClassName, *this); +} + /** * Returns the value of the property \a name, taking into account that it may * be inherited from another object or from the class. @@ -78,32 +89,36 @@ QVariant Object::resolvedProperty(const QString &name) const QVariantMap Object::resolvedProperties() const { QVariantMap allProperties; + Tiled::mergeProperties(allProperties, inheritedProperties()); + return allProperties; +} + +/** + * Computes the inherited properties for this object. This excludes the + * properties that are directly set on the object. + */ +QVariantMap Object::inheritedProperties() const +{ + QVariantMap inheritedProperties; + // Insert properties into allProperties in the reverse order that // Object::resolvedProperty searches them, to make sure that the // same precedence is maintained. - QString objectClassName = className(); - if (objectClassName.isEmpty() && typeId() == Object::MapObjectType) { - auto mapObject = static_cast(this); - objectClassName = mapObject->effectiveClassName(); - } - - if (auto type = propertyTypes().findClassFor(objectClassName, *this)) - Tiled::mergeProperties(allProperties, type->members); + if (auto type = classType()) + Tiled::mergeProperties(inheritedProperties, type->members); if (typeId() == Object::MapObjectType) { auto mapObject = static_cast(this); if (const Tile *tile = mapObject->cell().tile()) - Tiled::mergeProperties(allProperties, tile->properties()); + Tiled::mergeProperties(inheritedProperties, tile->properties()); if (const MapObject *templateObject = mapObject->templateObject()) - Tiled::mergeProperties(allProperties, templateObject->properties()); + Tiled::mergeProperties(inheritedProperties, templateObject->properties()); } - Tiled::mergeProperties(allProperties, properties()); - - return allProperties; + return inheritedProperties; } bool Object::setProperty(const QStringList &path, const QVariant &value) diff --git a/src/libtiled/object.h b/src/libtiled/object.h index 589fcbcd47..52b8592657 100644 --- a/src/libtiled/object.h +++ b/src/libtiled/object.h @@ -72,6 +72,8 @@ class TILEDSHARED_EXPORT Object const QString &className() const; void setClassName(const QString &className); + const ClassPropertyType *classType() const; + /** * Returns the properties of this object. */ @@ -111,6 +113,7 @@ class TILEDSHARED_EXPORT Object QVariant resolvedProperty(const QString &name) const; QVariantMap resolvedProperties() const; + QVariantMap inheritedProperties() const; /** * Returns the value of the object's \a name property, as a string. diff --git a/src/tiled/changeproperties.cpp b/src/tiled/changeproperties.cpp index 9b36a3a479..8814936668 100644 --- a/src/tiled/changeproperties.cpp +++ b/src/tiled/changeproperties.cpp @@ -164,8 +164,7 @@ void SetProperty::undo() void SetProperty::redo() { - const QList &objects = mObjects; - for (Object *obj : objects) + for (Object *obj : std::as_const(mObjects)) mDocument->setPropertyMember(obj, mPath, mValue); } @@ -212,8 +211,7 @@ void RemoveProperty::undo() void RemoveProperty::redo() { - const QList &objects = mObjects; - for (Object *obj : objects) + for (Object *obj : std::as_const(mObjects)) mDocument->removeProperty(obj, mName); } diff --git a/src/tiled/colorbutton.cpp b/src/tiled/colorbutton.cpp index 25c1d3751e..32358d4a23 100644 --- a/src/tiled/colorbutton.cpp +++ b/src/tiled/colorbutton.cpp @@ -22,7 +22,6 @@ #include "utils.h" -#include #include #include @@ -68,7 +67,8 @@ void ColorButton::changeEvent(QEvent *e) void ColorButton::pickColor() { - const QColor newColor = QColorDialog::getColor(mColor, this); + const QColor newColor = QColorDialog::getColor(mColor, this, QString(), + mDialogOptions); if (newColor.isValid()) setColor(newColor); } @@ -77,7 +77,7 @@ void ColorButton::updateIcon() { // todo: fix gray icon in disabled state (consider using opacity, and not using an icon at all) setIcon(Utils::colorIcon(mColor, iconSize())); - setText(mColor.isValid() ? QString() : tr("Unset")); + setText(mColor.isValid() ? QString() : tr("Not set")); } #include "moc_colorbutton.cpp" diff --git a/src/tiled/colorbutton.h b/src/tiled/colorbutton.h index b623dc772f..b32de0cc2a 100644 --- a/src/tiled/colorbutton.h +++ b/src/tiled/colorbutton.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include namespace Tiled { @@ -41,6 +42,9 @@ class ColorButton : public QToolButton QColor color() const { return mColor; } void setColor(const QColor &color); + void setShowAlphaChannel(bool enabled) + { mDialogOptions.setFlag(QColorDialog::ShowAlphaChannel, enabled); } + signals: void colorChanged(const QColor &color); @@ -52,6 +56,7 @@ class ColorButton : public QToolButton void updateIcon(); QColor mColor; + QColorDialog::ColorDialogOptions mDialogOptions; }; } // namespace Tiled diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index ba11667291..c1f9deb34b 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -418,10 +418,9 @@ class VariantMapProperty : public GroupProperty public: VariantMapProperty(const QString &name, QObject *parent = nullptr) : GroupProperty(name, parent) - { - } + {} - void setValue(const QVariantMap &value); + void setValue(const QVariantMap &value, const QVariantMap &suggestions = {}); const QVariantMap &value() const { return mValue; } signals: @@ -444,6 +443,7 @@ class VariantMapProperty : public GroupProperty void emitValueChangedRecursively(Property *property); QVariantMap mValue; + QVariantMap mSuggestions; QHash mPropertyMap; }; @@ -464,7 +464,31 @@ class CustomProperties : public VariantMapProperty private: // todo: optimize further + void onChanged(const ChangeEvent &change) { + if (change.type != ChangeEvent::ObjectsChanged) + return; + + auto object = mDocument->currentObject(); + if (!object) + return; + + auto &objectsChange = static_cast(change); + + if (objectsChange.properties & ObjectsChangeEvent::ClassProperty) { + if (objectsChange.objects.contains(object)) { + refresh(); + } else if (object->typeId() == Object::MapObjectType) { + auto mapObject = static_cast(object); + if (auto tile = mapObject->cell().tile()) { + if (mapObject->className().isEmpty() && objectsChange.objects.contains(tile)) + refresh(); + } + } + } + } void propertyAdded(Object *object, const QString &) { + if (mUpdating) + return; if (!objectPropertiesRelevant(mDocument, object)) return; refresh(); @@ -1196,6 +1220,7 @@ class ImageLayerProperties : public LayerProperties [this](const QColor &value) { push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value)); }); + mTransparentColorProperty->setAlpha(false); mRepeatProperty = new ImageLayerRepeatProperty( tr("Repeat"), @@ -1250,7 +1275,7 @@ class ImageLayerProperties : public LayerProperties GroupProperty *mImageLayerProperties; UrlProperty *mImageProperty; - Property *mTransparentColorProperty; + ColorProperty *mTransparentColorProperty; Property *mRepeatProperty; }; @@ -2227,19 +2252,24 @@ void PropertiesWidget::currentObjectChanged(Object *object) } -void VariantMapProperty::setValue(const QVariantMap &value) +void VariantMapProperty::setValue(const QVariantMap &value, + const QVariantMap &suggestions) { mValue = value; + mSuggestions = suggestions; + + QVariantMap allProperties = mSuggestions; + mergeProperties(allProperties, mValue); clear(); - QMapIterator it(mValue); + QMapIterator it(allProperties); while (it.hasNext()) { it.next(); const QString &name = it.key(); auto get = [=] { - return mValue.value(name); + return mValue.value(name, mSuggestions.value(name)); }; auto set = [=] (const QVariant &value) { mValue.insert(name, value); @@ -2248,7 +2278,12 @@ void VariantMapProperty::setValue(const QVariantMap &value) }; if (auto property = createProperty({ name }, std::move(get), std::move(set))) { - property->setActions(Property::Remove); + if (mValue.contains(name)) { + property->setActions(Property::Action::Remove); + } else { + property->setEnabled(false); + property->setActions(Property::Action::Add); + } updateModifiedRecursively(property, it.value()); @@ -2258,12 +2293,34 @@ void VariantMapProperty::setValue(const QVariantMap &value) connect(property, &Property::removeRequested, this, [=] { mValue.remove(name); - if (auto property = mPropertyMap.take(name)) - deleteProperty(property); + if (!mSuggestions.contains(name)) { + if (auto property = mPropertyMap.take(name)) + deleteProperty(property); + } else { + if (auto property = mPropertyMap.value(name)) { + property->setEnabled(false); + property->setActions(Property::Action::Add); + emitValueChangedRecursively(property); + updateModifiedRecursively(property, mSuggestions.value(name)); + } + } emit memberValueChanged({ name }, QVariant()); emit valueChanged(); }); + + connect(property, &Property::addRequested, this, [=] { + const auto memberValue = mSuggestions.value(name); + mValue.insert(name, memberValue); + + if (auto property = mPropertyMap.value(name)) { + property->setEnabled(true); + property->setActions(Property::Action::Remove); + } + + emit memberValueChanged({ name }, memberValue); + emit valueChanged(); + }); } } @@ -2364,7 +2421,7 @@ void VariantMapProperty::createClassMembers(const QStringList &path, }; if (auto childProperty = createProperty(childPath, std::move(getMember), setMember)) { - childProperty->setActions(Property::Reset); + childProperty->setActions(Property::Action::Reset); groupProperty->addProperty(childProperty); connect(childProperty, &Property::resetRequested, this, [=] { @@ -2416,6 +2473,8 @@ void CustomProperties::setDocument(Document *document) mDocument = document; if (document) { + connect(document, &Document::changed, this, &CustomProperties::onChanged); + connect(document, &Document::currentObjectsChanged, this, &CustomProperties::refresh); connect(document, &Document::propertyAdded, this, &CustomProperties::propertyAdded); @@ -2429,10 +2488,14 @@ void CustomProperties::setDocument(Document *document) void CustomProperties::refresh() { - if (mDocument && mDocument->currentObject()) - setValue(mDocument->currentObject()->properties()); - else + if (!mDocument || !mDocument->currentObject()) { setValue({}); + return; + } + + // todo: gather the values from all selected objects + setValue(mDocument->currentObject()->properties(), + mDocument->currentObject()->inheritedProperties()); } void CustomProperties::setPropertyValue(const QStringList &path, const QVariant &value) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 284248c9a2..1a50ca321b 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -545,14 +545,19 @@ void PropertyLabel::setModified(bool modified) setFont(f); } -void PropertyLabel::mousePressEvent(QMouseEvent *event) -{ - if (m_expandable && event->button() == Qt::LeftButton) { - setExpanded(!m_expanded); - return; +bool PropertyLabel::event(QEvent *event) +{ + // Handled here instead of in mousePressEvent because we want it to be + // expandable also when the label is disabled. + if (event->type() == QEvent::MouseButtonPress && m_expandable) { + auto mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) { + setExpanded(!m_expanded); + return true; + } } - ElidingLabel::mousePressEvent(event); + return ElidingLabel::event(event); } void PropertyLabel::paintEvent(QPaintEvent *event) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index cf3330ccd1..b29c1cf848 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -302,7 +302,7 @@ class PropertyLabel : public ElidingLabel void toggled(bool expanded); protected: - void mousePressEvent(QMouseEvent *event) override; + bool event(QEvent *event) override; void paintEvent(QPaintEvent *) override; private: diff --git a/src/tiled/scriptmodule.cpp b/src/tiled/scriptmodule.cpp index bc70d8edba..b60c6bf519 100644 --- a/src/tiled/scriptmodule.cpp +++ b/src/tiled/scriptmodule.cpp @@ -74,8 +74,8 @@ ScriptModule::ScriptModule(QObject *parent) ScriptModule::~ScriptModule() { - for (const auto &pair : mRegisteredActions) - ActionManager::unregisterAction(pair.second.get(), pair.first); + for (const auto &[id, action] : mRegisteredActions) + ActionManager::unregisterAction(action.get(), id); ActionManager::clearMenuExtensions(); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index a73a3831e1..6469775f20 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -65,6 +65,14 @@ void Property::setModified(bool modified) } } +void Property::setActions(Actions actions) +{ + if (m_actions != actions) { + m_actions = actions; + emit actionsChanged(actions); + } +} + void StringProperty::setPlaceholderText(const QString &placeholderText) { if (m_placeholderText != placeholderText) { @@ -328,10 +336,12 @@ QWidget *RectFProperty::createEditor(QWidget *parent) return editor; } -// todo: needs to handle invalid color (unset value) +// todo: needs to allow setting color back to invalid (unset) QWidget *ColorProperty::createEditor(QWidget *parent) { auto editor = new ColorButton(parent); + editor->setShowAlphaChannel(m_alpha); + auto syncEditor = [=] { const QSignalBlocker blocker(editor); editor->setColor(value()); @@ -507,8 +517,8 @@ void VariantEditor::clear() while (it.hasNext()) { it.next(); auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.layout); - delete widgets.layout; + Utils::deleteAllFromLayout(widgets.rowLayout); + delete widgets.rowLayout; delete widgets.children; it.key()->disconnect(this); @@ -531,97 +541,102 @@ void VariantEditor::addProperty(Property *property) const auto halfSpacing = Utils::dpiScaled(2); - widgets.layout = new QHBoxLayout; - widgets.layout->setSpacing(halfSpacing * 2); + widgets.rowLayout = new QHBoxLayout; + widgets.rowLayout->setSpacing(halfSpacing * 2); if (displayMode == Property::DisplayMode::Separator) { auto separator = new QFrame(this); - widgets.layout->setContentsMargins(0, halfSpacing, 0, halfSpacing); + widgets.rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); - widgets.layout->addWidget(separator); - m_layout->addLayout(widgets.layout); + widgets.rowLayout->addWidget(separator); + m_layout->addLayout(widgets.rowLayout); return; } - auto label = new PropertyLabel(m_level, this); + widgets.label = new PropertyLabel(m_level, this); if (displayMode != Property::DisplayMode::NoLabel) { - label->setText(property->name()); - label->setToolTip(property->toolTip()); - label->setEnabled(property->isEnabled()); - label->setModified(property->isModified()); - connect(property, &Property::toolTipChanged, label, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, label, &QWidget::setEnabled); - connect(property, &Property::modifiedChanged, label, &PropertyLabel::setModified); + widgets.label->setText(property->name()); + widgets.label->setModified(property->isModified()); + connect(property, &Property::modifiedChanged, widgets.label, &PropertyLabel::setModified); } if (displayMode == Property::DisplayMode::Header) - label->setHeader(true); + widgets.label->setHeader(true); else - widgets.layout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); - - widgets.layout->addWidget(label, LabelStretch, Qt::AlignTop); + widgets.rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); - QHBoxLayout *editorLayout = widgets.layout; - const auto editor = property->createEditor(this); + widgets.rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); - if (editor && property->actions()) { - editorLayout = new QHBoxLayout; - widgets.layout->addLayout(editorLayout, WidgetStretch); - } - - if (editor) { - editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - editor->setToolTip(property->toolTip()); - editor->setEnabled(property->isEnabled()); - connect(property, &Property::toolTipChanged, editor, &QWidget::setToolTip); - connect(property, &Property::enabledChanged, editor, &QWidget::setEnabled); + widgets.editorLayout = new QHBoxLayout; + widgets.editor = property->createEditor(this); - editorLayout->addWidget(editor, WidgetStretch, Qt::AlignTop); + if (widgets.editor) { + widgets.editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + widgets.editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); + widgets.rowLayout->addLayout(widgets.editorLayout, EditorStretch); + } else { + widgets.rowLayout->addLayout(widgets.editorLayout, 0); } - if (property->actions()) { - if (property->actions() & Property::Reset) { - auto resetButton = new QToolButton; - resetButton->setToolTip(tr("Reset")); - resetButton->setAutoRaise(true); - resetButton->setEnabled(property->isModified()); - Utils::setThemeIcon(resetButton, "edit-clear"); - editorLayout->addWidget(resetButton, 0, Qt::AlignTop); - connect(resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); - connect(property, &Property::modifiedChanged, resetButton, &QWidget::setEnabled); - } - - if (property->actions() & Property::Remove) { - auto removeButton = new QToolButton; - removeButton->setToolTip(tr("Remove")); - removeButton->setAutoRaise(true); - Utils::setThemeIcon(removeButton, "remove"); - editorLayout->addWidget(removeButton, 0, Qt::AlignTop); - connect(removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); - } - } + widgets.resetButton = new QToolButton(this); + widgets.resetButton->setToolTip(tr("Reset")); + widgets.resetButton->setAutoRaise(true); + widgets.resetButton->setEnabled(property->isModified()); + Utils::setThemeIcon(widgets.resetButton, "edit-clear"); + widgets.editorLayout->addWidget(widgets.resetButton, 0, Qt::AlignTop); + connect(widgets.resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); + connect(property, &Property::modifiedChanged, widgets.resetButton, &QWidget::setEnabled); + + widgets.removeButton = new QToolButton(this); + widgets.removeButton->setToolTip(tr("Remove")); + widgets.removeButton->setAutoRaise(true); + Utils::setThemeIcon(widgets.removeButton, "remove"); + widgets.editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); + connect(widgets.removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); + + widgets.addButton = new QToolButton(this); + widgets.addButton->setToolTip(tr("Add")); + widgets.addButton->setAutoRaise(true); + Utils::setThemeIcon(widgets.addButton, "add"); + widgets.editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); + connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); + + updatePropertyEnabled(widgets, property->isEnabled()); + updatePropertyToolTip(widgets, property->toolTip()); + updatePropertyActions(widgets, property->actions()); + + connect(property, &Property::enabledChanged, this, [=] (bool enabled) { + updatePropertyEnabled(m_propertyWidgets[property], enabled); + }); + connect(property, &Property::toolTipChanged, this, [=] (const QString &toolTip) { + updatePropertyToolTip(m_propertyWidgets[property], toolTip); + }); + connect(property, &Property::actionsChanged, this, [=] (Property::Actions actions) { + updatePropertyActions(m_propertyWidgets[property], actions); + }); - m_layout->addLayout(widgets.layout); + m_layout->addLayout(widgets.rowLayout); if (auto groupProperty = dynamic_cast(property)) { - label->setExpandable(true); - label->setExpanded(label->isHeader()); + widgets.label->setExpandable(true); + widgets.label->setExpanded(widgets.label->isHeader()); auto children = new VariantEditor(this); - if (label->isHeader()) + if (widgets.label->isHeader()) children->setContentsMargins(0, halfSpacing, 0, halfSpacing); children->setLevel(m_level + 1); - children->setVisible(label->isExpanded()); + children->setEnabled(property->isEnabled()); + children->setVisible(widgets.label->isExpanded()); for (auto property : groupProperty->subProperties()) children->addProperty(property); connect(groupProperty, &GroupProperty::propertyAdded, children, &VariantEditor::addProperty); - connect(label, &PropertyLabel::toggled, children, [=](bool expanded) { + connect(widgets.label, &PropertyLabel::toggled, children, [=](bool expanded) { children->setVisible(expanded); // needed to avoid flickering when hiding the editor @@ -645,10 +660,10 @@ void VariantEditor::removeProperty(Property *property) auto it = m_propertyWidgets.constFind(property); Q_ASSERT(it != m_propertyWidgets.constEnd()); - if (it != m_propertyWidgets.end()) { + if (it != m_propertyWidgets.constEnd()) { auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.layout); - delete widgets.layout; + Utils::deleteAllFromLayout(widgets.rowLayout); + delete widgets.rowLayout; delete widgets.children; m_propertyWidgets.erase(it); @@ -666,6 +681,31 @@ void VariantEditor::setLevel(int level) setAutoFillBackground(m_level > 1); } +void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) +{ + if (widgets.label) + widgets.label->setEnabled(enabled); + if (widgets.editor) + widgets.editor->setEnabled(enabled); + if (widgets.children) + widgets.children->setEnabled(enabled); +} + +void VariantEditor::updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip) +{ + if (widgets.label) + widgets.label->setToolTip(toolTip); + if (widgets.editor) + widgets.editor->setToolTip(toolTip); +} + +void VariantEditor::updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions) +{ + widgets.resetButton->setVisible(actions & Property::Action::Reset); + widgets.removeButton->setVisible(actions & Property::Action::Remove); + widgets.addButton->setVisible(actions & Property::Action::Add); +} + QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index c4dc66a928..9ef8f11ba8 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -30,6 +30,7 @@ #include class QHBoxLayout; +class QToolButton; class QVBoxLayout; namespace Tiled { @@ -46,6 +47,7 @@ class Property : public QObject Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged) + Q_PROPERTY(Actions actions READ actions WRITE setActions NOTIFY actionsChanged) public: enum class DisplayMode { @@ -55,9 +57,10 @@ class Property : public QObject Separator }; - enum Action { + enum class Action { Reset = 0x01, Remove = 0x02, + Add = 0x04, }; Q_DECLARE_FLAGS(Actions, Action) @@ -78,7 +81,7 @@ class Property : public QObject void setModified(bool modified); Actions actions() const { return m_actions; } - void setActions(Actions actions) { m_actions = actions; } + void setActions(Actions actions); virtual DisplayMode displayMode() const { return DisplayMode::Default; } @@ -89,9 +92,11 @@ class Property : public QObject void valueChanged(); void enabledChanged(bool enabled); void modifiedChanged(bool modified); + void actionsChanged(Actions actions); void resetRequested(); void removeRequested(); + void addRequested(); private: friend class GroupProperty; @@ -349,6 +354,11 @@ struct ColorProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; + + void setAlpha(bool alpha) { m_alpha = alpha; } + +private: + bool m_alpha = true; }; struct FontProperty : PropertyTemplate @@ -461,14 +471,24 @@ class VariantEditor : public QWidget private: static constexpr int LabelStretch = 4; - static constexpr int WidgetStretch = 6; + static constexpr int EditorStretch = 6; struct PropertyWidgets { - QHBoxLayout *layout = nullptr; + QHBoxLayout *rowLayout = nullptr; + QHBoxLayout *editorLayout = nullptr; + PropertyLabel *label = nullptr; + QWidget *editor = nullptr; + QToolButton *resetButton = nullptr; + QToolButton *removeButton = nullptr; + QToolButton *addButton = nullptr; QWidget *children = nullptr; }; + void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); + void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); + void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); + QVBoxLayout *m_layout; QHash m_propertyWidgets; int m_level = 0; From 75e24c326976367d644c490c85d2fdfb6beec246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 10 Oct 2024 16:38:23 +0200 Subject: [PATCH 33/78] Optimized updating of the list of custom properties * Now properties that have the same name and type are re-used, avoiding re-creation of editors for most property changes. This also avoids collapsing of nested properties in those cases. * Properties that change type, are re-created. We can still optimise the case where the current object changes further, because in this case all property widgets are still re-created because the CustomProperties object is re-added to the top-level VariantEditor instance. --- src/tiled/propertieswidget.cpp | 175 ++++++++++++++++++++++++--------- src/tiled/varianteditor.cpp | 112 ++++++++++++--------- src/tiled/varianteditor.h | 21 +++- 3 files changed, 212 insertions(+), 96 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index c1f9deb34b..ab6b785e8c 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -420,7 +420,9 @@ class VariantMapProperty : public GroupProperty : GroupProperty(name, parent) {} - void setValue(const QVariantMap &value, const QVariantMap &suggestions = {}); + void setValue(const QVariantMap &value, + const QVariantMap &suggestions = {}); + const QVariantMap &value() const { return mValue; } signals: @@ -430,6 +432,10 @@ class VariantMapProperty : public GroupProperty Document *mDocument = nullptr; private: + bool createOrUpdateProperty(int index, + const QString &name, + const QVariant &oldValue, + const QVariant &newValue); Property *createProperty(const QStringList &path, std::function get, std::function set); @@ -439,6 +445,9 @@ class VariantMapProperty : public GroupProperty const ClassPropertyType &classType, std::function get); + void removeMember(const QString &name); + void addMember(const QString &name, const QVariant &value); + void updateModifiedRecursively(Property *property, const QVariant &value); void emitValueChangedRecursively(Property *property); @@ -463,7 +472,6 @@ class CustomProperties : public VariantMapProperty void setDocument(Document *document); private: - // todo: optimize further void onChanged(const ChangeEvent &change) { if (change.type != ChangeEvent::ObjectsChanged) return; @@ -486,6 +494,7 @@ class CustomProperties : public VariantMapProperty } } } + void propertyAdded(Object *object, const QString &) { if (mUpdating) return; @@ -493,6 +502,7 @@ class CustomProperties : public VariantMapProperty return; refresh(); } + void propertyRemoved(Object *object, const QString &) { if (mUpdating) return; @@ -500,6 +510,7 @@ class CustomProperties : public VariantMapProperty return; refresh(); } + void propertyChanged(Object *object, const QString &name) { if (mUpdating) return; @@ -507,6 +518,7 @@ class CustomProperties : public VariantMapProperty return; refresh(); } + void propertiesChanged(Object *object) { if (!objectPropertiesRelevant(mDocument, object)) return; @@ -2252,22 +2264,76 @@ void PropertiesWidget::currentObjectChanged(Object *object) } +static bool isSameType(const QVariant &a, const QVariant &b) +{ + if (a.userType() != b.userType()) + return false; + + // Two PropertyValue values might still have different types + if (a.userType() == propertyValueId()) { + auto aTypeId = a.value().typeId; + auto bTypeId = b.value().typeId; + if (aTypeId != bTypeId) + return false; + } + + return true; +} + void VariantMapProperty::setValue(const QVariantMap &value, const QVariantMap &suggestions) { + QVariantMap oldProperties = mSuggestions; + mergeProperties(oldProperties, mValue); + mValue = value; mSuggestions = suggestions; - QVariantMap allProperties = mSuggestions; - mergeProperties(allProperties, mValue); + QVariantMap newProperties = suggestions; + mergeProperties(newProperties, value); - clear(); + // First, delete all properties that are not in allProperties + for (auto it = mPropertyMap.begin(); it != mPropertyMap.end(); ) { + if (newProperties.contains(it.key())) { + ++it; + } else { + deleteProperty(it.value()); + it = mPropertyMap.erase(it); + } + } - QMapIterator it(allProperties); + int index = 0; + + QMapIterator it(newProperties); while (it.hasNext()) { it.next(); const QString &name = it.key(); + const auto &newValue = it.value(); + const auto oldValue = oldProperties.value(name); + + if (createOrUpdateProperty(index, name, oldValue, newValue)) + ++index; + } + + emit valueChanged(); +} +bool VariantMapProperty::createOrUpdateProperty(int index, + const QString &name, + const QVariant &oldValue, + const QVariant &newValue) +{ + auto property = mPropertyMap.value(name); + + // If it already exists, check whether we need to delete it + if (property && !isSameType(oldValue, newValue)) { + deleteProperty(property); + mPropertyMap.remove(name); + property = nullptr; + } + + // Try to create the property if necessary + if (!property) { auto get = [=] { return mValue.value(name, mSuggestions.value(name)); }; @@ -2277,54 +2343,38 @@ void VariantMapProperty::setValue(const QVariantMap &value, emit valueChanged(); }; - if (auto property = createProperty({ name }, std::move(get), std::move(set))) { - if (mValue.contains(name)) { - property->setActions(Property::Action::Remove); - } else { - property->setEnabled(false); - property->setActions(Property::Action::Add); - } - - updateModifiedRecursively(property, it.value()); - - addProperty(property); - mPropertyMap.insert(name, property); - + property = createProperty({ name }, std::move(get), std::move(set)); + if (property) { connect(property, &Property::removeRequested, this, [=] { - mValue.remove(name); - - if (!mSuggestions.contains(name)) { - if (auto property = mPropertyMap.take(name)) - deleteProperty(property); - } else { - if (auto property = mPropertyMap.value(name)) { - property->setEnabled(false); - property->setActions(Property::Action::Add); - emitValueChangedRecursively(property); - updateModifiedRecursively(property, mSuggestions.value(name)); - } - } - - emit memberValueChanged({ name }, QVariant()); - emit valueChanged(); + removeMember(name); }); connect(property, &Property::addRequested, this, [=] { - const auto memberValue = mSuggestions.value(name); - mValue.insert(name, memberValue); + addMember(name, mSuggestions.value(name)); + }); - if (auto property = mPropertyMap.value(name)) { - property->setEnabled(true); - property->setActions(Property::Action::Remove); - } + insertProperty(index, property); + mPropertyMap.insert(name, property); + } else { + qWarning() << "Failed to create property for" << name + << "with type" << newValue.typeName(); + } + } - emit memberValueChanged({ name }, memberValue); - emit valueChanged(); - }); + if (property) { + if (mValue.contains(name)) { + property->setEnabled(true); + property->setActions(Property::Action::Remove); + } else { + property->setEnabled(false); + property->setActions(Property::Action::Add); } + + updateModifiedRecursively(property, newValue); + emitValueChangedRecursively(property); } - emit valueChanged(); + return true; } Property *VariantMapProperty::createProperty(const QStringList &path, @@ -2432,6 +2482,41 @@ void VariantMapProperty::createClassMembers(const QStringList &path, } } +void VariantMapProperty::removeMember(const QString &name) +{ + if (!mValue.contains(name)) + return; + + const auto oldValue = mValue.take(name); + + if (!mSuggestions.contains(name)) { + if (auto property = mPropertyMap.take(name)) + deleteProperty(property); + } else if (auto property = mPropertyMap.value(name)) { + const auto newValue = mSuggestions.value(name); + const int index = indexOfProperty(property); + createOrUpdateProperty(index, name, oldValue, newValue); + } + + emit memberValueChanged({ name }, QVariant()); + emit valueChanged(); +} + +void VariantMapProperty::addMember(const QString &name, const QVariant &value) +{ + const auto oldValue = mValue.value(name, mSuggestions.value(name)); + + mValue.insert(name, value); + + if (auto property = mPropertyMap.value(name)) { + const int index = indexOfProperty(property); + createOrUpdateProperty(index, name, oldValue, value); + } + + emit memberValueChanged({ name }, value); + emit valueChanged(); +} + void VariantMapProperty::updateModifiedRecursively(Property *property, const QVariant &value) { diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 6469775f20..11460fae13 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -517,9 +517,8 @@ void VariantEditor::clear() while (it.hasNext()) { it.next(); auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.rowLayout); - delete widgets.rowLayout; - delete widgets.children; + Utils::deleteAllFromLayout(widgets.layout); + delete widgets.layout; it.key()->disconnect(this); } @@ -527,10 +526,52 @@ void VariantEditor::clear() } /** - * Adds the given property to the editor. Does not take ownership of the - * property. + * Adds the given property. + * Does not take ownership of the property. */ void VariantEditor::addProperty(Property *property) +{ + m_layout->addLayout(createPropertyLayout(property)); +} + +/** + * Insert the given property at the given index. + * Does not take ownership of the property. + */ +void VariantEditor::insertProperty(int index, Property *property) +{ + m_layout->insertLayout(index, createPropertyLayout(property)); +} + +/** + * Removes the given property from the editor. The property is not deleted. + */ +void VariantEditor::removeProperty(Property *property) +{ + auto it = m_propertyWidgets.constFind(property); + Q_ASSERT(it != m_propertyWidgets.constEnd()); + + if (it != m_propertyWidgets.constEnd()) { + auto &widgets = it.value(); + Utils::deleteAllFromLayout(widgets.layout); + delete widgets.layout; + + m_propertyWidgets.erase(it); + } + + property->disconnect(this); +} + +void VariantEditor::setLevel(int level) +{ + m_level = level; + + setBackgroundRole((m_level % 2) ? QPalette::AlternateBase + : QPalette::Base); + setAutoFillBackground(m_level > 1); +} + +QLayout *VariantEditor::createPropertyLayout(Property *property) { auto &widgets = m_propertyWidgets[property]; const auto displayMode = property->displayMode(); @@ -541,18 +582,19 @@ void VariantEditor::addProperty(Property *property) const auto halfSpacing = Utils::dpiScaled(2); - widgets.rowLayout = new QHBoxLayout; - widgets.rowLayout->setSpacing(halfSpacing * 2); + auto rowLayout = new QHBoxLayout; + rowLayout->setSpacing(halfSpacing * 2); + + widgets.layout = rowLayout; if (displayMode == Property::DisplayMode::Separator) { auto separator = new QFrame(this); - widgets.rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); + rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); - widgets.rowLayout->addWidget(separator); - m_layout->addLayout(widgets.rowLayout); - return; + rowLayout->addWidget(separator); + return widgets.layout; } widgets.label = new PropertyLabel(m_level, this); @@ -566,9 +608,9 @@ void VariantEditor::addProperty(Property *property) if (displayMode == Property::DisplayMode::Header) widgets.label->setHeader(true); else - widgets.rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); + rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); - widgets.rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); + rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); widgets.editorLayout = new QHBoxLayout; widgets.editor = property->createEditor(this); @@ -576,9 +618,9 @@ void VariantEditor::addProperty(Property *property) if (widgets.editor) { widgets.editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); widgets.editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); - widgets.rowLayout->addLayout(widgets.editorLayout, EditorStretch); + rowLayout->addLayout(widgets.editorLayout, EditorStretch); } else { - widgets.rowLayout->addLayout(widgets.editorLayout, 0); + rowLayout->addLayout(widgets.editorLayout, 0); } widgets.resetButton = new QToolButton(this); @@ -618,9 +660,9 @@ void VariantEditor::addProperty(Property *property) updatePropertyActions(m_propertyWidgets[property], actions); }); - m_layout->addLayout(widgets.rowLayout); - if (auto groupProperty = dynamic_cast(property)) { + auto verticalLayout = new QVBoxLayout; + widgets.label->setExpandable(true); widgets.label->setExpanded(widgets.label->isHeader()); @@ -634,7 +676,7 @@ void VariantEditor::addProperty(Property *property) children->addProperty(property); connect(groupProperty, &GroupProperty::propertyAdded, - children, &VariantEditor::addProperty); + children, &VariantEditor::insertProperty); connect(widgets.label, &PropertyLabel::toggled, children, [=](bool expanded) { children->setVisible(expanded); @@ -647,38 +689,14 @@ void VariantEditor::addProperty(Property *property) } }); - widgets.children = children; - m_layout->addWidget(widgets.children); - } -} - -/** - * Removes the given property from the editor. The property is not deleted. - */ -void VariantEditor::removeProperty(Property *property) -{ - auto it = m_propertyWidgets.constFind(property); - Q_ASSERT(it != m_propertyWidgets.constEnd()); + verticalLayout->addLayout(rowLayout); + verticalLayout->addWidget(children); - if (it != m_propertyWidgets.constEnd()) { - auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.rowLayout); - delete widgets.rowLayout; - delete widgets.children; - - m_propertyWidgets.erase(it); + widgets.children = children; + widgets.layout = verticalLayout; } - property->disconnect(this); -} - -void VariantEditor::setLevel(int level) -{ - m_level = level; - - setBackgroundRole((m_level % 2) ? QPalette::AlternateBase - : QPalette::Base); - setAutoFillBackground(m_level > 1); + return widgets.layout; } void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 9ef8f11ba8..fe571a2a03 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -151,8 +151,13 @@ class GroupProperty : public Property void addProperty(Property *property) { - m_subProperties.append(property); - emit propertyAdded(property); + insertProperty(m_subProperties.size(), property); + } + + void insertProperty(int index, Property *property) + { + m_subProperties.insert(index, property); + emit propertyAdded(index, property); } void deleteProperty(Property *property) @@ -161,12 +166,17 @@ class GroupProperty : public Property delete property; } + int indexOfProperty(Property *property) const + { + return m_subProperties.indexOf(property); + } + void addSeparator() { addProperty(new Separator(this)); } const QList &subProperties() const { return m_subProperties; } signals: - void propertyAdded(Property *property); + void propertyAdded(int index, Property *property); private: bool m_header = true; @@ -465,6 +475,7 @@ class VariantEditor : public QWidget void clear(); void addProperty(Property *property); + void insertProperty(int index, Property *property); void removeProperty(Property *property); void setLevel(int level); @@ -475,7 +486,7 @@ class VariantEditor : public QWidget struct PropertyWidgets { - QHBoxLayout *rowLayout = nullptr; + QLayout *layout = nullptr; QHBoxLayout *editorLayout = nullptr; PropertyLabel *label = nullptr; QWidget *editor = nullptr; @@ -485,6 +496,8 @@ class VariantEditor : public QWidget QWidget *children = nullptr; }; + QLayout *createPropertyLayout(Property *property); + void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); From 02eda6ead7554ff6d18922019c17883f0495cd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 10 Oct 2024 21:53:17 +0200 Subject: [PATCH 34/78] Optimized PropertyLabel to not need a QLineEdit instance It was only used for calculating the size hint, which is now done with some code copied and adjusted from QLineEdit::sizeHint. --- src/tiled/propertyeditorwidgets.cpp | 36 ++++++++++++++++++++++------- src/tiled/propertyeditorwidgets.h | 4 ++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 1a50ca321b..c0b9e6ebd3 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -501,11 +501,7 @@ PropertyLabel::PropertyLabel(int level, QWidget *parent) void PropertyLabel::setLevel(int level) { m_level = level; - - const int spacing = Utils::dpiScaled(3); - const int branchIndicatorWidth = Utils::dpiScaled(14); - setContentsMargins(spacing + branchIndicatorWidth * std::max(m_level, 1), - spacing, spacing, spacing); + updateContentMargins(); } void PropertyLabel::setHeader(bool header) @@ -517,6 +513,7 @@ void PropertyLabel::setHeader(bool header) setBackgroundRole(header ? QPalette::Dark : QPalette::NoRole); setForegroundRole(header ? QPalette::BrightText : QPalette::NoRole); setAutoFillBackground(header); + updateContentMargins(); } void PropertyLabel::setExpandable(bool expandable) @@ -588,12 +585,35 @@ void PropertyLabel::paintEvent(QPaintEvent *event) } } +void PropertyLabel::updateContentMargins() +{ + const int spacing = Utils::dpiScaled(3); + const int branchIndicatorWidth = Utils::dpiScaled(14); + const int verticalSpacing = m_header ? spacing : 0; + setContentsMargins(spacing + branchIndicatorWidth * std::max(m_level, 1), + verticalSpacing, spacing, verticalSpacing); + +} +/** + * To fit better alongside other widgets without vertical centering, the size + * hint is adjusted to match that of a QLineEdit. + */ QSize PropertyLabel::sizeHint() const { - auto hint = ElidingLabel::sizeHint(); - hint.setHeight(m_lineEdit.sizeHint().height()); - return hint; + constexpr int QLineEditPrivate_verticalMargin = 1; + constexpr int QLineEditPrivate_horizontalMargin = 2; + + auto fm = fontMetrics(); + auto cm = contentsMargins(); + const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this); + int h = qMax(fm.height(), qMax(14, iconSize - 2)) + 2 * QLineEditPrivate_verticalMargin + + cm.top() + cm.bottom(); + int w = fm.horizontalAdvance(u'x') * 17 + 2 * QLineEditPrivate_horizontalMargin + + cm.left() + cm.right(); + QStyleOptionFrame opt; + initStyleOption(&opt); + return style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h), this); } } // namespace Tiled diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index b29c1cf848..6c3722798d 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -21,7 +21,6 @@ #pragma once #include -#include #include class QLabel; @@ -306,7 +305,8 @@ class PropertyLabel : public ElidingLabel void paintEvent(QPaintEvent *) override; private: - QLineEdit m_lineEdit; + void updateContentMargins(); + int m_level = 0; bool m_header = false; bool m_expandable = false; From deb0a0966c227d05a6faaa0cef638f0cc1de2b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 13:24:45 +0200 Subject: [PATCH 35/78] Don't display label on checkbox for boolean properties For now it still does this for some common built-in properties, but no longer for custom properties. --- src/tiled/propertieswidget.cpp | 12 ++++++++---- src/tiled/varianteditor.cpp | 13 ++++++++++++- src/tiled/varianteditor.h | 7 ++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index ab6b785e8c..262f7bfd8f 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -857,6 +857,7 @@ class MapProperties : public ObjectProperties Map::InfiniteProperty, value ? 1 : 0)); }); + mInfiniteProperty->setNameOnCheckBox(true); mHexSideLengthProperty = new IntProperty( tr("Hex Side Length"), @@ -1062,7 +1063,7 @@ class MapProperties : public ObjectProperties Property *mOrientationProperty; Property *mSizeProperty; SizeProperty *mTileSizeProperty; - Property *mInfiniteProperty; + BoolProperty *mInfiniteProperty; IntProperty *mHexSideLengthProperty; Property *mStaggerAxisProperty; Property *mStaggerIndexProperty; @@ -1104,6 +1105,7 @@ class LayerProperties : public ObjectProperties [this](const bool &value) { push(new SetLayerVisible(mapDocument(), { layer() }, value)); }); + mVisibleProperty->setNameOnCheckBox(true); mLockedProperty = new BoolProperty( tr("Locked"), @@ -1111,6 +1113,7 @@ class LayerProperties : public ObjectProperties [this](const bool &value) { push(new SetLayerLocked(mapDocument(), { layer() }, value)); }); + mLockedProperty->setNameOnCheckBox(true); mOpacityProperty = new IntProperty( tr("Opacity"), @@ -1202,8 +1205,8 @@ class LayerProperties : public ObjectProperties GroupProperty *mLayerProperties; Property *mIdProperty; Property *mNameProperty; - Property *mVisibleProperty; - Property *mLockedProperty; + BoolProperty *mVisibleProperty; + BoolProperty *mLockedProperty; IntProperty *mOpacityProperty; Property *mTintColorProperty; Property *mOffsetProperty; @@ -1635,6 +1638,7 @@ class MapObjectProperties : public ObjectProperties [this](const bool &value) { changeMapObject(MapObject::VisibleProperty, value); }); + mVisibleProperty->setNameOnCheckBox(true); mPositionProperty = new PointFProperty( tr("Position"), @@ -1846,7 +1850,7 @@ class MapObjectProperties : public ObjectProperties Property *mIdProperty; Property *mTemplateProperty; Property *mNameProperty; - Property *mVisibleProperty; + BoolProperty *mVisibleProperty; Property *mPositionProperty; Property *mSizeProperty; FloatProperty *mRotationProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 11460fae13..ec400d1066 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -192,6 +192,11 @@ QWidget *FloatProperty::createEditor(QWidget *parent) return editor; } +Property::DisplayMode BoolProperty::displayMode() const +{ + return m_nameOnCheckBox ? DisplayMode::NoLabel : DisplayMode::Default; +} + QWidget *BoolProperty::createEditor(QWidget *parent) { auto editor = new QCheckBox(name(), parent); @@ -199,6 +204,8 @@ QWidget *BoolProperty::createEditor(QWidget *parent) const QSignalBlocker blocker(editor); bool checked = value(); editor->setChecked(checked); + if (!m_nameOnCheckBox) + editor->setText(checked ? tr("On") : tr("Off")); // Reflect modified state on the checkbox, since we're not showing the // property label. @@ -210,7 +217,11 @@ QWidget *BoolProperty::createEditor(QWidget *parent) connect(this, &Property::valueChanged, editor, syncEditor); connect(this, &Property::modifiedChanged, editor, syncEditor); - connect(editor, &QCheckBox::toggled, this, &BoolProperty::setValue); + connect(editor, &QCheckBox::toggled, this, [=](bool checked) { + if (!m_nameOnCheckBox) + editor->setText(checked ? QObject::tr("On") : QObject::tr("Off")); + setValue(checked); + }); return editor; } diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index fe571a2a03..08cafc779f 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -291,8 +291,13 @@ struct FloatProperty : PropertyTemplate struct BoolProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; - DisplayMode displayMode() const override { return DisplayMode::NoLabel; } + DisplayMode displayMode() const override; QWidget *createEditor(QWidget *parent) override; + + void setNameOnCheckBox(bool nameOnCheckBox) { m_nameOnCheckBox = nameOnCheckBox; } + +private: + bool m_nameOnCheckBox = false; }; struct PointProperty : PropertyTemplate From b301505e8d297c1ab317c79431bedacae3baeff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 13:26:15 +0200 Subject: [PATCH 36/78] Create child property widgets on-demand as they are expanded Might be necessary for performance reasons, because the amount of widgets created could quickly get out of hand. --- src/tiled/varianteditor.cpp | 59 ++++++++++++++++++++++--------------- src/tiled/varianteditor.h | 5 +++- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index ec400d1066..19f2fd2154 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -657,6 +657,19 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); + if (auto groupProperty = dynamic_cast(property)) { + widgets.childrenLayout = new QVBoxLayout; + widgets.childrenLayout->addLayout(rowLayout); + widgets.layout = widgets.childrenLayout; + + connect(widgets.label, &PropertyLabel::toggled, this, [=](bool expanded) { + setPropertyChildrenExpanded(groupProperty, expanded); + }); + + widgets.label->setExpandable(true); + widgets.label->setExpanded(widgets.label->isHeader()); + } + updatePropertyEnabled(widgets, property->isEnabled()); updatePropertyToolTip(widgets, property->toolTip()); updatePropertyActions(widgets, property->actions()); @@ -671,43 +684,43 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) updatePropertyActions(m_propertyWidgets[property], actions); }); - if (auto groupProperty = dynamic_cast(property)) { - auto verticalLayout = new QVBoxLayout; + return widgets.layout; +} - widgets.label->setExpandable(true); - widgets.label->setExpanded(widgets.label->isHeader()); +void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded) +{ + auto &widgets = m_propertyWidgets[groupProperty]; + + // Create the children editor on-demand + if (expanded && !widgets.children) { + const auto halfSpacing = Utils::dpiScaled(2); - auto children = new VariantEditor(this); + widgets.children = new VariantEditor(this); if (widgets.label->isHeader()) - children->setContentsMargins(0, halfSpacing, 0, halfSpacing); - children->setLevel(m_level + 1); - children->setEnabled(property->isEnabled()); - children->setVisible(widgets.label->isExpanded()); + widgets.children->setContentsMargins(0, halfSpacing, 0, halfSpacing); + widgets.children->setLevel(m_level + 1); + widgets.children->setEnabled(groupProperty->isEnabled()); for (auto property : groupProperty->subProperties()) - children->addProperty(property); + widgets.children->addProperty(property); connect(groupProperty, &GroupProperty::propertyAdded, - children, &VariantEditor::insertProperty); + widgets.children, &VariantEditor::insertProperty); - connect(widgets.label, &PropertyLabel::toggled, children, [=](bool expanded) { - children->setVisible(expanded); + widgets.childrenLayout->addWidget(widgets.children); + } + + if (widgets.children) { + widgets.children->setVisible(expanded); - // needed to avoid flickering when hiding the editor + // needed to avoid flickering when hiding the editor + if (!expanded) { QWidget *widget = this; while (widget && widget->layout()) { widget->layout()->activate(); widget = widget->parentWidget(); } - }); - - verticalLayout->addLayout(rowLayout); - verticalLayout->addWidget(children); - - widgets.children = children; - widgets.layout = verticalLayout; + } } - - return widgets.layout; } void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 08cafc779f..e8bc5f64e4 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -493,16 +493,19 @@ class VariantEditor : public QWidget { QLayout *layout = nullptr; QHBoxLayout *editorLayout = nullptr; + QVBoxLayout *childrenLayout = nullptr; PropertyLabel *label = nullptr; QWidget *editor = nullptr; QToolButton *resetButton = nullptr; QToolButton *removeButton = nullptr; QToolButton *addButton = nullptr; - QWidget *children = nullptr; + VariantEditor *children = nullptr; }; QLayout *createPropertyLayout(Property *property); + void setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded); + void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); From 82ab2cb6ed442b776ced88dbed45e4e654758257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 13:28:18 +0200 Subject: [PATCH 37/78] Handle changes in the custom property types Now any properties using custom types are re-created when any change is made to the custom types. --- src/tiled/propertieswidget.cpp | 83 ++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 262f7bfd8f..04d779ed55 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -416,9 +416,7 @@ class VariantMapProperty : public GroupProperty Q_OBJECT public: - VariantMapProperty(const QString &name, QObject *parent = nullptr) - : GroupProperty(name, parent) - {} + VariantMapProperty(const QString &name, QObject *parent = nullptr); void setValue(const QVariantMap &value, const QVariantMap &suggestions = {}); @@ -451,6 +449,12 @@ class VariantMapProperty : public GroupProperty void updateModifiedRecursively(Property *property, const QVariant &value); void emitValueChangedRecursively(Property *property); + void propertyTypesChanged(); + + void emitMemberValueChanged(const QStringList &path, const QVariant &value); + + bool mEmittingValueChanged = false; + bool mPropertyTypesChanged = false; QVariantMap mValue; QVariantMap mSuggestions; QHash mPropertyMap; @@ -2268,20 +2272,11 @@ void PropertiesWidget::currentObjectChanged(Object *object) } -static bool isSameType(const QVariant &a, const QVariant &b) +VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) + : GroupProperty(name, parent) { - if (a.userType() != b.userType()) - return false; - - // Two PropertyValue values might still have different types - if (a.userType() == propertyValueId()) { - auto aTypeId = a.value().typeId; - auto bTypeId = b.value().typeId; - if (aTypeId != bTypeId) - return false; - } - - return true; + connect(Preferences::instance(), &Preferences::propertyTypesChanged, + this, &VariantMapProperty::propertyTypesChanged); } void VariantMapProperty::setValue(const QVariantMap &value, @@ -2319,9 +2314,32 @@ void VariantMapProperty::setValue(const QVariantMap &value, ++index; } + QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); emit valueChanged(); } +static bool canReuseProperty(const QVariant &a, + const QVariant &b, + bool propertyTypesChanged) +{ + if (a.userType() != b.userType()) + return false; + + // Two PropertyValue values might still have different types + if (a.userType() == propertyValueId()) { + // Trigger re-creation of the property when the types have changed + if (propertyTypesChanged) + return false; + + auto aTypeId = a.value().typeId; + auto bTypeId = b.value().typeId; + if (aTypeId != bTypeId) + return false; + } + + return true; +} + bool VariantMapProperty::createOrUpdateProperty(int index, const QString &name, const QVariant &oldValue, @@ -2330,7 +2348,7 @@ bool VariantMapProperty::createOrUpdateProperty(int index, auto property = mPropertyMap.value(name); // If it already exists, check whether we need to delete it - if (property && !isSameType(oldValue, newValue)) { + if (property && !canReuseProperty(oldValue, newValue, mPropertyTypesChanged)) { deleteProperty(property); mPropertyMap.remove(name); property = nullptr; @@ -2343,8 +2361,7 @@ bool VariantMapProperty::createOrUpdateProperty(int index, }; auto set = [=] (const QVariant &value) { mValue.insert(name, value); - emit memberValueChanged({ name }, value); - emit valueChanged(); + emitMemberValueChanged({ name }, value); }; property = createProperty({ name }, std::move(get), std::move(set)); @@ -2469,8 +2486,7 @@ void VariantMapProperty::createClassMembers(const QStringList &path, updateModifiedRecursively(mPropertyMap.value(topLevelName), mValue.value(topLevelName)); - emit memberValueChanged(childPath, value); - emit valueChanged(); + emitMemberValueChanged(childPath, value); } }; @@ -2502,8 +2518,7 @@ void VariantMapProperty::removeMember(const QString &name) createOrUpdateProperty(index, name, oldValue, newValue); } - emit memberValueChanged({ name }, QVariant()); - emit valueChanged(); + emitMemberValueChanged({ name }, QVariant()); } void VariantMapProperty::addMember(const QString &name, const QVariant &value) @@ -2517,8 +2532,7 @@ void VariantMapProperty::addMember(const QString &name, const QVariant &value) createOrUpdateProperty(index, name, oldValue, value); } - emit memberValueChanged({ name }, value); - emit valueChanged(); + emitMemberValueChanged({ name }, value); } void VariantMapProperty::updateModifiedRecursively(Property *property, @@ -2550,6 +2564,25 @@ void VariantMapProperty::emitValueChangedRecursively(Property *property) emitValueChangedRecursively(subProperty); } +void VariantMapProperty::propertyTypesChanged() +{ + // When this happens in response to emitting value changed, it means we + // have triggered a change in a class definition. In this case we should + // not update ourselves. + if (mEmittingValueChanged) + return; + + QScopedValueRollback emittingValueChanged(mPropertyTypesChanged, true); + setValue(mValue, mSuggestions); +} + +void VariantMapProperty::emitMemberValueChanged(const QStringList &path, const QVariant &value) +{ + QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); + emit memberValueChanged(path, value); + emit valueChanged(); +} + void CustomProperties::setDocument(Document *document) { From 8cc03747e89e77fad096f19efcb4007c2c97d30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 13:28:56 +0200 Subject: [PATCH 38/78] Collect property values from all selected objects In order to allow them to be easily added, when they are not set on the current object. --- src/tiled/propertieswidget.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 04d779ed55..8131c8ad0c 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2615,9 +2615,19 @@ void CustomProperties::refresh() return; } - // todo: gather the values from all selected objects - setValue(mDocument->currentObject()->properties(), - mDocument->currentObject()->inheritedProperties()); + const Properties ¤tObjectProperties = mDocument->currentObject()->properties(); + + // Suggest properties from selected objects. + Properties suggestedProperties; + for (auto object : mDocument->currentObjects()) + if (object != mDocument->currentObject()) + mergeProperties(suggestedProperties, object->properties()); + + // Suggest properties inherited from the class, tile or template. + mergeProperties(suggestedProperties, + mDocument->currentObject()->inheritedProperties()); + + setValue(currentObjectProperties, suggestedProperties); } void CustomProperties::setPropertyValue(const QStringList &path, const QVariant &value) From 629b53cea8dbc4ae46c9447a80b27c310b17e50f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 17:08:37 +0200 Subject: [PATCH 39/78] Made layer properties apply to all selected layers Same for the more specific object layer and image layer properties. --- src/tiled/propertieswidget.cpp | 63 ++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 8131c8ad0c..594962aa1d 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1094,20 +1094,22 @@ class LayerProperties : public ObjectProperties [this] { return layer()->id(); }); mIdProperty->setEnabled(false); - // todo: the below should be able to apply to all selected layers - mNameProperty = new StringProperty( tr("Name"), [this] { return layer()->name(); }, [this](const QString &value) { - push(new SetLayerName(mapDocument(), { layer() }, value)); + push(new SetLayerName(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mVisibleProperty = new BoolProperty( tr("Visible"), [this] { return layer()->isVisible(); }, [this](const bool &value) { - push(new SetLayerVisible(mapDocument(), { layer() }, value)); + push(new SetLayerVisible(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mVisibleProperty->setNameOnCheckBox(true); @@ -1115,7 +1117,9 @@ class LayerProperties : public ObjectProperties tr("Locked"), [this] { return layer()->isLocked(); }, [this](const bool &value) { - push(new SetLayerLocked(mapDocument(), { layer() }, value)); + push(new SetLayerLocked(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mLockedProperty->setNameOnCheckBox(true); @@ -1123,7 +1127,9 @@ class LayerProperties : public ObjectProperties tr("Opacity"), [this] { return qRound(layer()->opacity() * 100); }, [this](const int &value) { - push(new SetLayerOpacity(mapDocument(), { layer() }, qreal(value) / 100)); + push(new SetLayerOpacity(mapDocument(), + mapDocument()->selectedLayers(), + qreal(value) / 100)); }); mOpacityProperty->setRange(0, 100); mOpacityProperty->setSuffix(tr("%")); @@ -1133,21 +1139,29 @@ class LayerProperties : public ObjectProperties tr("Tint Color"), [this] { return layer()->tintColor(); }, [this](const QColor &value) { - push(new SetLayerTintColor(mapDocument(), { layer() }, value)); + push(new SetLayerTintColor(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mOffsetProperty = new PointFProperty( tr("Offset"), [this] { return layer()->offset(); }, [this](const QPointF &value) { - push(new SetLayerOffset(mapDocument(), { layer() }, value)); + // todo: consider whether we can apply only the changed axis to the selected layers + // this is what PropertyBrowser::applyLayerValue used to do + push(new SetLayerOffset(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mParallaxFactorProperty = new PointFProperty( tr("Parallax Factor"), [this] { return layer()->parallaxFactor(); }, [this](const QPointF &value) { - push(new SetLayerParallaxFactor(mapDocument(), { layer() }, value)); + push(new SetLayerParallaxFactor(mapDocument(), + mapDocument()->selectedLayers(), + value)); }); mParallaxFactorProperty->setSingleStep(0.1); @@ -1206,6 +1220,20 @@ class LayerProperties : public ObjectProperties return static_cast(mObject); } + template + QList selectedLayersOfType(Layer::TypeFlag typeFlag) + { + if (mDocument) + return {}; + + QList result; + for (Layer *layer : mapDocument()->selectedLayers()) + if (layer->layerType() == typeFlag) + result.append(static_cast(layer)); + + return result; + } + GroupProperty *mLayerProperties; Property *mIdProperty; Property *mNameProperty; @@ -1229,7 +1257,8 @@ class ImageLayerProperties : public LayerProperties tr("Image Source"), [this] { return imageLayer()->imageSource(); }, [this](const QUrl &value) { - push(new ChangeImageLayerImageSource(mapDocument(), { imageLayer() }, value)); + const auto imageLayers = selectedLayersOfType(Layer::ImageLayerType); + push(new ChangeImageLayerImageSource(mapDocument(), imageLayers, value)); }); mImageProperty->setFilter(Utils::readableImageFormatsFilter()); @@ -1237,7 +1266,8 @@ class ImageLayerProperties : public LayerProperties tr("Transparent Color"), [this] { return imageLayer()->transparentColor(); }, [this](const QColor &value) { - push(new ChangeImageLayerTransparentColor(mapDocument(), { imageLayer() }, value)); + const auto imageLayers = selectedLayersOfType(Layer::ImageLayerType); + push(new ChangeImageLayerTransparentColor(mapDocument(), imageLayers, value)); }); mTransparentColorProperty->setAlpha(false); @@ -1247,10 +1277,11 @@ class ImageLayerProperties : public LayerProperties [this](const ImageLayer::RepetitionFlags &value) { const bool repeatX = value & ImageLayer::RepeatX; const bool repeatY = value & ImageLayer::RepeatY; + const auto imageLayers = selectedLayersOfType(Layer::ImageLayerType); if (repeatX != imageLayer()->repeatX()) - push(new ChangeImageLayerRepeatX(mapDocument(), { imageLayer() }, repeatX)); + push(new ChangeImageLayerRepeatX(mapDocument(), imageLayers, repeatX)); if (repeatY != imageLayer()->repeatY()) - push(new ChangeImageLayerRepeatY(mapDocument(), { imageLayer() }, repeatY)); + push(new ChangeImageLayerRepeatY(mapDocument(), imageLayers, repeatY)); }); mImageLayerProperties = new GroupProperty(tr("Image Layer")); @@ -1310,14 +1341,16 @@ class ObjectGroupProperties : public LayerProperties tr("Color"), [this] { return objectGroup()->color(); }, [this](const QColor &value) { - push(new ChangeObjectGroupColor(mapDocument(), { objectGroup() }, value)); + const auto objectGroups = selectedLayersOfType(Layer::ObjectGroupType); + push(new ChangeObjectGroupColor(mapDocument(), objectGroups, value)); }); mDrawOrderProperty = new EnumProperty( tr("Draw Order"), [this] { return objectGroup()->drawOrder(); }, [this](ObjectGroup::DrawOrder value) { - push(new ChangeObjectGroupDrawOrder(mapDocument(), { objectGroup() }, value)); + const auto objectGroups = selectedLayersOfType(Layer::ObjectGroupType); + push(new ChangeObjectGroupDrawOrder(mapDocument(), objectGroups, value)); }); mObjectGroupProperties = new GroupProperty(tr("Object Layer")); From 9e0c00157f8d62989b91141fee5af97f9555f812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 17:11:12 +0200 Subject: [PATCH 40/78] Added a custom widget for the object flipping property Using two toggle buttons rather than checkboxes, similar to the "Allowed Transformations" tileset property. Also changed the stretch factor of the property labels column to be the same as the property editor column. It looks more balanced overall and helps to keep the labels readable when propeties are nested a few levels. --- src/tiled/propertieswidget.cpp | 62 ++++++++++++++++++++++++++++++++-- src/tiled/varianteditor.h | 5 ++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 594962aa1d..e9c0bda044 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -199,6 +199,65 @@ template<> EnumData enumData() } +class FlippingProperty : public IntProperty +{ + Q_OBJECT + +public: + using IntProperty::IntProperty; + + QWidget *createEditor(QWidget *parent) override + { + QIcon flipHorizontalIcon(QLatin1String(":images/24/flip-horizontal.png")); + QIcon flipVerticalIcon(QLatin1String(":images/24/flip-vertical.png")); + + flipHorizontalIcon.addFile(QLatin1String(":images/32/flip-horizontal.png")); + flipVerticalIcon.addFile(QLatin1String(":images/32/flip-vertical.png")); + + auto editor = new QWidget(parent); + + auto flipHorizontally = new QToolButton(editor); + flipHorizontally->setToolTip(tr("Flip Horizontally")); + flipHorizontally->setIcon(flipHorizontalIcon); + flipHorizontally->setCheckable(true); + + auto flipVertically = new QToolButton(editor); + flipVertically->setToolTip(tr("Flip Vertically")); + flipVertically->setIcon(flipVerticalIcon); + flipVertically->setCheckable(true); + + auto horizontalLayout = new QHBoxLayout(editor); + horizontalLayout->setContentsMargins(QMargins()); + horizontalLayout->addWidget(flipHorizontally); + horizontalLayout->addWidget(flipVertically); + horizontalLayout->addStretch(); + + auto syncEditor = [=] { + const QSignalBlocker horizontalBlocker(flipHorizontally); + const QSignalBlocker verticalBlocker(flipVertically); + const auto v = value(); + flipHorizontally->setChecked(v & Cell::FlippedHorizontally); + flipVertically->setChecked(v & Cell::FlippedVertically); + }; + auto syncProperty = [=] { + int flags = 0; + if (flipHorizontally->isChecked()) + flags |= Cell::FlippedHorizontally; + if (flipVertically->isChecked()) + flags |= Cell::FlippedVertically; + setValue(flags); + }; + + syncEditor(); + + connect(this, &Property::valueChanged, editor, syncEditor); + connect(flipHorizontally, &QAbstractButton::toggled, this, syncProperty); + connect(flipVertically, &QAbstractButton::toggled, this, syncProperty); + return editor; + } +}; + + class ObjectRefProperty : public PropertyTemplate { Q_OBJECT @@ -1706,8 +1765,7 @@ class MapObjectProperties : public ObjectProperties this); mRotationProperty->setSuffix(QStringLiteral("°")); - // todo: make this a custom widget with "Horizontal" and "Vertical" checkboxes - mFlippingProperty = new IntProperty( + mFlippingProperty = new FlippingProperty( tr("Flipping"), [this] { return mapObject()->cell().flags(); diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index e8bc5f64e4..b21d2df0f3 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -364,7 +364,6 @@ struct RectFProperty : PropertyTemplate QWidget *createEditor(QWidget *parent) override; }; -// todo: needs to handle invalid color (unset value) struct ColorProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; @@ -486,8 +485,8 @@ class VariantEditor : public QWidget void setLevel(int level); private: - static constexpr int LabelStretch = 4; - static constexpr int EditorStretch = 6; + static constexpr int LabelStretch = 1; + static constexpr int EditorStretch = 1; struct PropertyWidgets { From ed94a0047c3cc3ebac00af59bfb0da541acc6552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 11 Oct 2024 17:42:48 +0200 Subject: [PATCH 41/78] Apply changes to map objects to all selected objects Also made the changes to layer offset and object flipping more specific, only changing the actually changing axis for all selected layers or objects respectively. The same still needs to be done for changing object position or size. --- src/tiled/propertieswidget.cpp | 71 +++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index e9c0bda044..e1eb88527b 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1207,11 +1207,25 @@ class LayerProperties : public ObjectProperties tr("Offset"), [this] { return layer()->offset(); }, [this](const QPointF &value) { - // todo: consider whether we can apply only the changed axis to the selected layers - // this is what PropertyBrowser::applyLayerValue used to do + const auto oldValue = layer()->offset(); + const bool changedX = oldValue.x() != value.x(); + const bool changedY = oldValue.y() != value.y(); + + QVector offsets; + for (const Layer *layer : mapDocument()->selectedLayers()) + offsets.append(layer->offset()); + + if (changedX) { + for (QPointF &offset : offsets) + offset.setX(value.x()); + } else if (changedY) { + for (QPointF &offset : offsets) + offset.setY(value.y()); + } + push(new SetLayerOffset(mapDocument(), mapDocument()->selectedLayers(), - value)); + offsets)); }); mParallaxFactorProperty = new PointFProperty( @@ -1771,20 +1785,29 @@ class MapObjectProperties : public ObjectProperties return mapObject()->cell().flags(); }, [this](const int &value) { - const int flippingFlags = value; - - MapObjectCell mapObjectCell; - mapObjectCell.object = mapObject(); - mapObjectCell.cell = mapObject()->cell(); - mapObjectCell.cell.setFlippedHorizontally(flippingFlags & 1); - mapObjectCell.cell.setFlippedVertically(flippingFlags & 2); + const int oldValue = mapObject()->cell().flags(); + const bool changedHorizontally = (oldValue & 1) != (value & 1); + const bool changedVertically = (oldValue & 2) != (value & 2); + + QVector objectChanges; + + for (MapObject *object : mapDocument()->selectedObjects()) { + MapObjectCell mapObjectCell; + mapObjectCell.object = object; + mapObjectCell.cell = object->cell(); + if (changedHorizontally) + mapObjectCell.cell.setFlippedHorizontally(value & 1); + if (changedVertically) + mapObjectCell.cell.setFlippedVertically(value & 2); + objectChanges.append(mapObjectCell); + } - auto command = new ChangeMapObjectCells(mDocument, { mapObjectCell }); + auto command = new ChangeMapObjectCells(mDocument, objectChanges); command->setText(QCoreApplication::translate("Undo Commands", "Flip %n Object(s)", nullptr, - mapDocument()->selectedObjects().size())); + objectChanges.size())); push(command); }); @@ -1938,7 +1961,29 @@ class MapObjectProperties : public ObjectProperties void changeMapObject(MapObject::Property property, const QVariant &value) { - push(new ChangeMapObject(mapDocument(), mapObject(), property, value)); + // todo: when changing object position or size, only change the + // selected objects in the respective axis or dimension + QUndoCommand *command = new ChangeMapObject(mapDocument(), mapObject(), + property, value); + + if (mapDocument()->selectedObjects().size() == 1) { + push(command); + return; + } + + auto undoStack = mDocument->undoStack(); + undoStack->beginMacro(command->text()); + undoStack->push(command); + + for (MapObject *obj : mapDocument()->selectedObjects()) { + if (obj != mapObject()) { + QUndoCommand *cmd = new ChangeMapObject(mapDocument(), obj, + property, value); + undoStack->push(cmd); + } + } + + mDocument->undoStack()->endMacro(); } GroupProperty *mObjectProperties; From da05ecf3f4a669ecc2253b7648f1cf005a0a24e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 14 Oct 2024 18:12:21 +0200 Subject: [PATCH 42/78] Show the type of custom properties in their tool tip Also fixed some issues in left-to-right mode. --- src/tiled/propertieswidget.cpp | 37 +++++++++++++------ src/tiled/propertyeditorwidgets.cpp | 55 ++++++++++++++++++++++------- src/tiled/propertyeditorwidgets.h | 3 ++ src/tiled/varianteditor.cpp | 4 ++- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index e1eb88527b..39c23d602a 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2538,19 +2538,20 @@ Property *VariantMapProperty::createProperty(const QStringList &path, std::function get, std::function set) { + Property *property = nullptr; + const auto value = get(); const auto type = value.userType(); const auto &name = path.last(); + QString typeName; if (type == filePathTypeId()) { auto getUrl = [get = std::move(get)] { return get().value().url; }; auto setUrl = [set = std::move(set)] (const QUrl &value) { set(QVariant::fromValue(FilePath { value })); }; - return new UrlProperty(name, std::move(getUrl), std::move(setUrl)); - } - - if (type == objectRefTypeId()) { + property = new UrlProperty(name, std::move(getUrl), std::move(setUrl)); + } else if (type == objectRefTypeId()) { auto getObjectRef = [get = std::move(get), this] { return DisplayObjectRef(get().value(), static_cast(mDocument)); @@ -2558,10 +2559,8 @@ Property *VariantMapProperty::createProperty(const QStringList &path, auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { set(QVariant::fromValue(value.ref)); }; - return new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); - } - - if (type == propertyValueId()) { + property = new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); + } else if (type == propertyValueId()) { const auto propertyValue = value.value(); if (auto propertyType = propertyValue.type()) { switch (propertyType->type) { @@ -2575,7 +2574,8 @@ Property *VariantMapProperty::createProperty(const QStringList &path, createClassMembers(path, groupProperty, classType, std::move(get)); - return groupProperty; + property = groupProperty; + break; } case PropertyType::PT_Enum: { auto enumProperty = new BaseEnumProperty( @@ -2589,13 +2589,28 @@ Property *VariantMapProperty::createProperty(const QStringList &path, enumProperty->setEnumData(enumType.values); enumProperty->setFlags(enumType.valuesAsFlags); - return enumProperty; + property = enumProperty; + break; } } + + typeName = propertyType->name; + } else { + typeName = tr("Unknown type"); } + } else { + property = createVariantProperty(name, std::move(get), std::move(set)); + } + + if (property) { + if (typeName.isEmpty()) + typeName = typeToName(type); + + property->setToolTip(QStringLiteral("%1 : %2") + .arg(property->name(), typeName)); } - return createVariantProperty(name, std::move(get), std::move(set)); + return property; } void VariantMapProperty::createClassMembers(const QStringList &path, diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index c0b9e6ebd3..bab7b58d70 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -459,6 +459,25 @@ ElidingLabel::ElidingLabel(const QString &text, QWidget *parent) setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); } +/** + * Sets a tool tip on the label. + * + * When a tool tip is set, it will be shown instead of the on-demand tool tip + * that shows the full text when the text is elided. + */ +void ElidingLabel::setToolTip(const QString &toolTip) +{ + if (m_toolTip == toolTip) + return; + + m_toolTip = toolTip; + + if (m_toolTip.isEmpty()) + QLabel::setToolTip(m_isElided ? text() : QString()); + else + QLabel::setToolTip(m_toolTip); +} + QSize ElidingLabel::minimumSizeHint() const { auto hint = QLabel::minimumSizeHint(); @@ -468,22 +487,23 @@ QSize ElidingLabel::minimumSizeHint() const void ElidingLabel::paintEvent(QPaintEvent *) { - const int m = margin(); - const QRect cr = contentsRect().adjusted(m, m, -m, -m); - const Qt::LayoutDirection dir = text().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; - const int align = QStyle::visualAlignment(dir, alignment()); - const int flags = align | (dir == Qt::LeftToRight ? Qt::TextForceLeftToRight - : Qt::TextForceRightToLeft); - QStyleOption opt; opt.initFrom(this); + const int m = margin(); + const QRect cr = contentsRect().adjusted(m, m, -m, -m); + const int align = QStyle::visualAlignment(opt.direction, alignment()); + const int flags = align | (opt.direction == Qt::LeftToRight ? Qt::TextForceLeftToRight + : Qt::TextForceRightToLeft); + const auto elidedText = opt.fontMetrics.elidedText(text(), Qt::ElideRight, cr.width()); const bool isElided = elidedText != text(); if (isElided != m_isElided) { m_isElided = isElided; - setToolTip(isElided ? text() : QString()); + + if (m_toolTip.isEmpty()) + QLabel::setToolTip(m_isElided ? text() : QString()); } QStylePainter p(this); @@ -554,6 +574,9 @@ bool PropertyLabel::event(QEvent *event) } } + if (event->type() == QEvent::LayoutDirectionChange) + updateContentMargins(); + return ElidingLabel::event(event); } @@ -563,11 +586,16 @@ void PropertyLabel::paintEvent(QPaintEvent *event) const int spacing = Utils::dpiScaled(3); const int branchIndicatorWidth = Utils::dpiScaled(14); + const int indent = branchIndicatorWidth * std::max(m_level - 1, 0); QStyleOption branchOption; branchOption.initFrom(this); - branchOption.rect = QRect(branchIndicatorWidth * std::max(m_level - 1, 0), 0, - branchIndicatorWidth + spacing, height()); + if (branchOption.direction == Qt::LeftToRight) + branchOption.rect = QRect(indent, 0, + branchIndicatorWidth + spacing, height()); + else + branchOption.rect = QRect(width() - indent - branchIndicatorWidth - spacing, 0, + branchIndicatorWidth + spacing, height()); if (m_expandable) branchOption.state |= QStyle::State_Children; if (m_expanded) @@ -590,9 +618,12 @@ void PropertyLabel::updateContentMargins() const int spacing = Utils::dpiScaled(3); const int branchIndicatorWidth = Utils::dpiScaled(14); const int verticalSpacing = m_header ? spacing : 0; - setContentsMargins(spacing + branchIndicatorWidth * std::max(m_level, 1), - verticalSpacing, spacing, verticalSpacing); + const int indent = branchIndicatorWidth * std::max(m_level, 1); + if (isLeftToRight()) + setContentsMargins(spacing + indent, verticalSpacing, spacing, verticalSpacing); + else + setContentsMargins(spacing, verticalSpacing, spacing + indent, verticalSpacing); } /** diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 6c3722798d..400018d3b7 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -263,12 +263,15 @@ class ElidingLabel : public QLabel explicit ElidingLabel(QWidget *parent = nullptr); ElidingLabel(const QString &text, QWidget *parent = nullptr); + void setToolTip(const QString &toolTip); + QSize minimumSizeHint() const override; protected: void paintEvent(QPaintEvent *) override; private: + QString m_toolTip; bool m_isElided = false; }; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 19f2fd2154..be4862808b 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -618,8 +618,10 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) if (displayMode == Property::DisplayMode::Header) widgets.label->setHeader(true); - else + else if (isLeftToRight()) rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); + else + rowLayout->setContentsMargins(halfSpacing * 2, halfSpacing, 0, halfSpacing); rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); From 58d7a3532ef135d5f74144951a3f96ed6aa5f561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 15 Oct 2024 14:40:48 +0200 Subject: [PATCH 43/78] Fixed the visibility of custom properties on the project --- src/tiled/propertieswidget.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 39c23d602a..a0b1b1d18d 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2394,8 +2394,10 @@ void PropertiesWidget::currentObjectChanged(Object *object) } } - if (mPropertiesObject) { - mPropertiesObject->populateEditor(mPropertyBrowser); + if (object) { + if (mPropertiesObject) + mPropertiesObject->populateEditor(mPropertyBrowser); + mPropertyBrowser->addProperty(mCustomProperties); } From bd54309be87315178701e9f6ee16d2373ee5146b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 17 Oct 2024 15:31:10 +0200 Subject: [PATCH 44/78] Remember the expanded state of custom class properties The expanded state is now remembered for each "path", meaning you can select different objects, and when they happen to have the same properties the expanded state applies to all of them. For now the state is lost when Tiled is closed. It might be nice to store the list of expanded property paths in the session. --- src/tiled/propertieswidget.cpp | 10 ++++++++++ src/tiled/varianteditor.cpp | 12 +++++++++++- src/tiled/varianteditor.h | 6 ++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index a0b1b1d18d..6a231a6167 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -517,6 +517,7 @@ class VariantMapProperty : public GroupProperty QVariantMap mValue; QVariantMap mSuggestions; QHash mPropertyMap; + QSet mExpandedProperties; }; @@ -2576,6 +2577,15 @@ Property *VariantMapProperty::createProperty(const QStringList &path, createClassMembers(path, groupProperty, classType, std::move(get)); + groupProperty->setExpanded(mExpandedProperties.contains(path)); + + connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { + if (expanded) + mExpandedProperties.insert(path); + else + mExpandedProperties.remove(path); + }); + property = groupProperty; break; } diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index be4862808b..6b401288bb 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -73,6 +73,14 @@ void Property::setActions(Actions actions) } } +void GroupProperty::setExpanded(bool expanded) +{ + if (m_expanded != expanded) { + m_expanded = expanded; + emit expandedChanged(expanded); + } +} + void StringProperty::setPlaceholderText(const QString &placeholderText) { if (m_placeholderText != placeholderText) { @@ -664,12 +672,14 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.childrenLayout->addLayout(rowLayout); widgets.layout = widgets.childrenLayout; + connect(groupProperty, &GroupProperty::expandedChanged, widgets.label, &PropertyLabel::setExpanded); connect(widgets.label, &PropertyLabel::toggled, this, [=](bool expanded) { setPropertyChildrenExpanded(groupProperty, expanded); + groupProperty->setExpanded(expanded); }); widgets.label->setExpandable(true); - widgets.label->setExpanded(widgets.label->isHeader()); + widgets.label->setExpanded(groupProperty->isExpanded()); } updatePropertyEnabled(widgets, property->isEnabled()); diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index b21d2df0f3..8afc1dd1b5 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -128,6 +128,7 @@ class Separator final : public Property class GroupProperty : public Property { Q_OBJECT + Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) public: GroupProperty(const QString &name, QObject *parent = nullptr) @@ -143,6 +144,9 @@ class GroupProperty : public Property void setHeader(bool header) { m_header = header; } + bool isExpanded() const { return m_expanded; } + void setExpanded(bool expanded); + void clear() { qDeleteAll(m_subProperties); @@ -176,10 +180,12 @@ class GroupProperty : public Property const QList &subProperties() const { return m_subProperties; } signals: + void expandedChanged(bool expanded); void propertyAdded(int index, Property *property); private: bool m_header = true; + bool m_expanded = true; QList m_subProperties; }; From c982c20ff71df44ba8ac8d2db823d175e2e0f852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 17 Oct 2024 17:56:28 +0200 Subject: [PATCH 45/78] Added context menu to custom properties Currently providing access to the existing Add, Remove and Reset actions as well as "Expand All" and "Collapse All" for custom classes. This also adds the fallback icons to the existing action buttons. --- src/tiled/propertieswidget.cpp | 84 +++++++++++++++++++++++++---- src/tiled/propertyeditorwidgets.cpp | 5 ++ src/tiled/propertyeditorwidgets.h | 1 + src/tiled/varianteditor.cpp | 31 ++++++++++- src/tiled/varianteditor.h | 8 +++ 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 6a231a6167..46bb119686 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -504,6 +504,7 @@ class VariantMapProperty : public GroupProperty void removeMember(const QString &name); void addMember(const QString &name, const QVariant &value); + void setClassMember(const QStringList &path, const QVariant &value); void updateModifiedRecursively(Property *property, const QVariant &value); void emitValueChangedRecursively(Property *property); @@ -512,6 +513,11 @@ class VariantMapProperty : public GroupProperty void emitMemberValueChanged(const QStringList &path, const QVariant &value); + void memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos); + + QIcon m_resetIcon; + QIcon m_removeIcon; + QIcon m_addIcon; bool mEmittingValueChanged = false; bool mPropertyTypesChanged = false; QVariantMap mValue; @@ -2413,7 +2419,14 @@ void PropertiesWidget::currentObjectChanged(Object *object) VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) : GroupProperty(name, parent) + , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) + , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) + , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) { + m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); + m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); + connect(Preferences::instance(), &Preferences::propertyTypesChanged, this, &VariantMapProperty::propertyTypesChanged); } @@ -2620,6 +2633,10 @@ Property *VariantMapProperty::createProperty(const QStringList &path, property->setToolTip(QStringLiteral("%1 : %2") .arg(property->name(), typeName)); + + connect(property, &Property::contextMenuRequested, this, [=](const QPoint &globalPos) { + memberContextMenuRequested(property, path, globalPos); + }); } return property; @@ -2644,13 +2661,7 @@ void VariantMapProperty::createClassMembers(const QStringList &path, return get().value().value.toMap().value(name, def); }; auto setMember = [=] (const QVariant &value) { - if (setPropertyMemberValue(mValue, childPath, value)) { - const auto &topLevelName = childPath.first(); - updateModifiedRecursively(mPropertyMap.value(topLevelName), - mValue.value(topLevelName)); - - emitMemberValueChanged(childPath, value); - } + setClassMember(childPath, value); }; if (auto childProperty = createProperty(childPath, std::move(getMember), setMember)) { @@ -2698,10 +2709,22 @@ void VariantMapProperty::addMember(const QString &name, const QVariant &value) emitMemberValueChanged({ name }, value); } +void VariantMapProperty::setClassMember(const QStringList &path, const QVariant &value) +{ + if (!setPropertyMemberValue(mValue, path, value)) + return; + + const auto &topLevelName = path.first(); + updateModifiedRecursively(mPropertyMap.value(topLevelName), + mValue.value(topLevelName)); + + emitMemberValueChanged(path, value); +} + void VariantMapProperty::updateModifiedRecursively(Property *property, const QVariant &value) { - auto groupProperty = dynamic_cast(property); + auto groupProperty = qobject_cast(property); if (!groupProperty) return; @@ -2722,7 +2745,7 @@ void VariantMapProperty::emitValueChangedRecursively(Property *property) { emit property->valueChanged(); - if (auto groupProperty = dynamic_cast(property)) + if (auto groupProperty = qobject_cast(property)) for (auto subProperty : groupProperty->subProperties()) emitValueChangedRecursively(subProperty); } @@ -2746,6 +2769,49 @@ void VariantMapProperty::emitMemberValueChanged(const QStringList &path, const Q emit valueChanged(); } +void VariantMapProperty::memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos) +{ + QMenu menu; + + // Add Expand All and Collapse All actions to group properties + if (auto groupProperty = qobject_cast(property)) { + menu.addAction(tr("Expand All"), groupProperty, &GroupProperty::expandAll); + menu.addAction(tr("Collapse All"), groupProperty, &GroupProperty::collapseAll); + } + + // Provide the Add, Remove and Reset actions also here + if (property->actions()) { + menu.addSeparator(); + + if (property->actions() & Property::Action::Add) { + QAction *add = menu.addAction(m_addIcon, tr("Add Property"), this, [this, name = path.first()] { + addMember(name, mSuggestions.value(name)); + }); + Utils::setThemeIcon(add, "add"); + } + if (property->actions() & Property::Action::Remove) { + QAction *remove = menu.addAction(m_removeIcon, tr("Remove Property"), this, [this, name = path.first()] { + removeMember(name); + }); + Utils::setThemeIcon(remove, "remove"); + } + if (property->actions() & Property::Action::Reset) { + QAction *reset = menu.addAction(m_resetIcon, tr("Reset Member"), this, [=] { + setClassMember(path, QVariant()); + emitValueChangedRecursively(property); + }); + reset->setEnabled(property->isModified()); + Utils::setThemeIcon(reset, "edit-clear"); + } + } + + // todo: Add "Convert" sub-menu + // todo: Add "Copy" and "Paste" actions + + if (!menu.isEmpty()) + menu.exec(globalPos); +} + void CustomProperties::setDocument(Document *document) { diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index bab7b58d70..3e81048ef1 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -574,6 +574,11 @@ bool PropertyLabel::event(QEvent *event) } } + if (event->type() == QEvent::ContextMenu) { + emit contextMenuRequested(static_cast(event)->globalPos()); + return true; + } + if (event->type() == QEvent::LayoutDirectionChange) updateContentMargins(); diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 400018d3b7..e5c98a51eb 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -302,6 +302,7 @@ class PropertyLabel : public ElidingLabel signals: void toggled(bool expanded); + void contextMenuRequested(const QPoint &globalPos); protected: bool event(QEvent *event) override; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 6b401288bb..4c9f90d167 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -81,6 +81,22 @@ void GroupProperty::setExpanded(bool expanded) } } +void GroupProperty::expandAll() +{ + setExpanded(true); + for (auto property : m_subProperties) + if (auto groupProperty = qobject_cast(property)) + groupProperty->expandAll(); +} + +void GroupProperty::collapseAll() +{ + setExpanded(false); + for (auto property : m_subProperties) + if (auto groupProperty = qobject_cast(property)) + groupProperty->collapseAll(); +} + void StringProperty::setPlaceholderText(const QString &placeholderText) { if (m_placeholderText != placeholderText) { @@ -520,7 +536,14 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) VariantEditor::VariantEditor(QWidget *parent) : QWidget(parent) + , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) + , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) + , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) { + m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); + m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); + m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(QMargins()); @@ -618,6 +641,9 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.label = new PropertyLabel(m_level, this); + connect(widgets.label, &PropertyLabel::contextMenuRequested, + property, &Property::contextMenuRequested); + if (displayMode != Property::DisplayMode::NoLabel) { widgets.label->setText(property->name()); widgets.label->setModified(property->isModified()); @@ -646,6 +672,7 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.resetButton = new QToolButton(this); widgets.resetButton->setToolTip(tr("Reset")); + widgets.resetButton->setIcon(m_resetIcon); widgets.resetButton->setAutoRaise(true); widgets.resetButton->setEnabled(property->isModified()); Utils::setThemeIcon(widgets.resetButton, "edit-clear"); @@ -655,6 +682,7 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.removeButton = new QToolButton(this); widgets.removeButton->setToolTip(tr("Remove")); + widgets.removeButton->setIcon(m_removeIcon); widgets.removeButton->setAutoRaise(true); Utils::setThemeIcon(widgets.removeButton, "remove"); widgets.editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); @@ -662,12 +690,13 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) widgets.addButton = new QToolButton(this); widgets.addButton->setToolTip(tr("Add")); + widgets.addButton->setIcon(m_addIcon); widgets.addButton->setAutoRaise(true); Utils::setThemeIcon(widgets.addButton, "add"); widgets.editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); - if (auto groupProperty = dynamic_cast(property)) { + if (auto groupProperty = qobject_cast(property)) { widgets.childrenLayout = new QVBoxLayout; widgets.childrenLayout->addLayout(rowLayout); widgets.layout = widgets.childrenLayout; diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 8afc1dd1b5..596db0ab76 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,8 @@ class Property : public QObject void removeRequested(); void addRequested(); + void contextMenuRequested(const QPoint &globalPos); + private: friend class GroupProperty; @@ -146,6 +149,8 @@ class GroupProperty : public Property bool isExpanded() const { return m_expanded; } void setExpanded(bool expanded); + void expandAll(); + void collapseAll(); void clear() { @@ -515,6 +520,9 @@ class VariantEditor : public QWidget void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); + QIcon m_resetIcon; + QIcon m_removeIcon; + QIcon m_addIcon; QVBoxLayout *m_layout; QHash m_propertyWidgets; int m_level = 0; From 35a7ca16a978338695c44621145f607699c9accf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 24 Oct 2024 13:36:09 +0200 Subject: [PATCH 46/78] Moved CSV layer data format up for consistency with New Map dialog --- src/tiled/propertieswidget.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 46bb119686..95ffe5a95d 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -127,12 +127,14 @@ template<> EnumData enumData() template<> EnumData enumData() { QStringList names { + QCoreApplication::translate("PreferencesDialog", "CSV"), QCoreApplication::translate("PreferencesDialog", "XML (deprecated)"), QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)"), QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)"), QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)"), }; QList values { + Map::CSV, Map::XML, Map::Base64, Map::Base64Gzip, @@ -144,9 +146,6 @@ template<> EnumData enumData() values.append(Map::Base64Zstandard); } - names.append(QCoreApplication::translate("PreferencesDialog", "CSV")); - values.append(Map::CSV); - return { names, values }; } From fae849c77beeb1db91065faa6003c81c0c9b82af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 24 Oct 2024 16:45:51 +0200 Subject: [PATCH 47/78] Added rename action to property context menu --- src/tiled/propertieswidget.cpp | 54 +++++++++++++++------------------- src/tiled/propertieswidget.h | 3 +- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 95ffe5a95d..42766919a8 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -483,6 +483,7 @@ class VariantMapProperty : public GroupProperty signals: void memberValueChanged(const QStringList &path, const QVariant &value); + void renameRequested(const QString &name); protected: Document *mDocument = nullptr; @@ -517,6 +518,7 @@ class VariantMapProperty : public GroupProperty QIcon m_resetIcon; QIcon m_removeIcon; QIcon m_addIcon; + QIcon m_renameIcon; bool mEmittingValueChanged = false; bool mPropertyTypesChanged = false; QVariantMap mValue; @@ -636,8 +638,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mActionRenameProperty = new QAction(this); mActionRenameProperty->setEnabled(false); mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); - connect(mActionRenameProperty, &QAction::triggered, - this, &PropertiesWidget::renameProperty); + // connect(mActionRenameProperty, &QAction::triggered, + // this, &PropertiesWidget::renameProperty); Utils::setThemeIcon(mActionAddProperty, "add"); Utils::setThemeIcon(mActionRemoveProperty, "remove"); @@ -664,6 +666,9 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, // this, &PropertiesWidget::updateActions); + connect(mCustomProperties, &VariantMapProperty::renameRequested, + this, &PropertiesWidget::renameProperty); + retranslateUi(); } @@ -2421,6 +2426,7 @@ VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) + , m_renameIcon(QIcon(QLatin1String(":/images/16/rename.png"))) { m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); @@ -2793,6 +2799,11 @@ void VariantMapProperty::memberContextMenuRequested(Property *property, const QS removeMember(name); }); Utils::setThemeIcon(remove, "remove"); + + // If a property can be removed, it can also be renamed + menu.addAction(m_renameIcon, tr("Rename Property..."), this, [this, name = path.first()] { + emit renameRequested(name); + }); } if (property->actions() & Property::Action::Reset) { QAction *reset = menu.addAction(m_resetIcon, tr("Reset Member"), this, [=] { @@ -3023,43 +3034,26 @@ void PropertiesWidget::removeProperties() #endif } -void PropertiesWidget::renameProperty() +void PropertiesWidget::renameProperty(const QString &name) { -#if 0 - QtBrowserItem *item = mPropertyBrowser->currentItem(); - if (!mPropertyBrowser->isCustomPropertyItem(item)) - return; - - const QString oldName = item->property()->propertyName(); - QInputDialog *dialog = new QInputDialog(mPropertyBrowser); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(QCoreApplication::translate("Tiled::PropertiesDock", "Name:")); - dialog->setTextValue(oldName); + dialog->setTextValue(name); dialog->setWindowTitle(QCoreApplication::translate("Tiled::PropertiesDock", "Rename Property")); - connect(dialog, &QInputDialog::textValueSelected, this, &PropertiesWidget::renamePropertyTo); - dialog->open(); -#endif -} -void PropertiesWidget::renamePropertyTo(const QString &name) -{ -#if 0 - if (name.isEmpty()) - return; - - QtBrowserItem *item = mPropertyBrowser->currentItem(); - if (!item) - return; + connect(dialog, &QInputDialog::textValueSelected, this, [=] (const QString &newName) { + if (newName.isEmpty()) + return; + if (newName == name) + return; - const QString oldName = item->property()->propertyName(); - if (oldName == name) - return; + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->push(new RenameProperty(mDocument, mDocument->currentObjects(), name, newName)); + }); - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new RenameProperty(mDocument, mDocument->currentObjects(), oldName, name)); -#endif + dialog->open(); } void PropertiesWidget::showContextMenu(const QPoint &pos) diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index a38f10ddd4..0eea6bd412 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -71,8 +71,7 @@ public slots: void openAddPropertyDialog(); void addProperty(const QString &name, const QVariant &value); void removeProperties(); - void renameProperty(); - void renamePropertyTo(const QString &name); + void renameProperty(const QString &name); void showContextMenu(const QPoint &pos); void retranslateUi(); From 56b4debf5f124d5bdfc6e8246d1f06bf8de8edb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 24 Oct 2024 18:00:00 +0200 Subject: [PATCH 48/78] Spin boxes and combo boxes ignore wheel events when not focused Otherwise it is too easy to accidentally trigger them while scrolling through the Properties view. --- src/tiled/propertieswidget.cpp | 4 +-- src/tiled/propertyeditorwidgets.cpp | 42 +++++++++++++++++++++++++++++ src/tiled/propertyeditorwidgets.h | 26 ++++++++++++++++-- src/tiled/varianteditor.cpp | 9 +++---- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 42766919a8..93a4618dcb 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -40,6 +40,7 @@ #include "objecttemplate.h" #include "preferences.h" #include "propertybrowser.h" +#include "propertyeditorwidgets.h" #include "tilesetchanges.h" #include "tilesetdocument.h" #include "tilesetparametersedit.h" @@ -50,7 +51,6 @@ #include #include -#include #include #include #include @@ -757,7 +757,7 @@ class ClassNameProperty : public StringProperty QWidget *createEditor(QWidget *parent) override { - auto editor = new QComboBox(parent); + auto editor = new ComboBox(parent); editor->setEditable(true); editor->lineEdit()->setPlaceholderText(placeholderText()); editor->addItems(classNamesFor(*mObject)); diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 3e81048ef1..9d86ae31df 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -55,6 +55,26 @@ static QString removeRedundantTrialingZeros(const QString &text) } +ComboBox::ComboBox(QWidget *parent) + : QComboBox(parent) +{ + // Combo boxes in properties view don't adjust to their contents + setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + + // Don't take focus by mouse wheel + if (focusPolicy() == Qt::WheelFocus) + setFocusPolicy(Qt::StrongFocus); +} + +void ComboBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QComboBox::wheelEvent(event); +} + + SpinBox::SpinBox(QWidget *parent) : QSpinBox(parent) { @@ -62,6 +82,9 @@ SpinBox::SpinBox(QWidget *parent) setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); + // Don't take focus by mouse wheel + setFocusPolicy(Qt::StrongFocus); + // Don't respond to keyboard input immediately. setKeyboardTracking(false); @@ -77,6 +100,14 @@ QSize SpinBox::minimumSizeHint() const return hint; } +void SpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSpinBox::wheelEvent(event); +} + DoubleSpinBox::DoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent) @@ -88,6 +119,9 @@ DoubleSpinBox::DoubleSpinBox(QWidget *parent) // Increase possible precision. setDecimals(9); + // Don't take focus by mouse wheel + setFocusPolicy(Qt::StrongFocus); + // Don't respond to keyboard input immediately. setKeyboardTracking(false); @@ -114,6 +148,14 @@ QString DoubleSpinBox::textFromValue(double val) const return text; } +void DoubleSpinBox::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QDoubleSpinBox::wheelEvent(event); +} + ResponsivePairswiseWidget::ResponsivePairswiseWidget(QWidget *parent) : QWidget(parent) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index e5c98a51eb..bd320bcf62 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -20,6 +20,7 @@ #pragma once +#include #include #include @@ -27,9 +28,24 @@ class QLabel; namespace Tiled { +/** + * A combo box that doesn't respond to wheel events when not focused. + */ +class ComboBox : public QComboBox +{ + Q_OBJECT + +public: + ComboBox(QWidget *parent = nullptr); + +protected: + void wheelEvent(QWheelEvent *event) override; +}; + /** * A spin box that allows the full range by default and shrinks horizontally. - * It also doesn't adjust the horizontal size hint based on the maximum value. + * It also doesn't adjust the horizontal size hint based on the maximum value + * and doesn't respond to wheel events when not focused. */ class SpinBox : public QSpinBox { @@ -39,12 +55,15 @@ class SpinBox : public QSpinBox SpinBox(QWidget *parent = nullptr); QSize minimumSizeHint() const override; + +protected: + void wheelEvent(QWheelEvent *event) override; }; /** * A double spin box that allows the full range by default and shrinks * horizontally. It also doesn't adjust the horizontal size hint based on the - * maximum value. + * maximum value and doesn't respond to wheel events when not focused. * * The precision is increased to 9 decimal places. Redundant trailing 0's are * removed. @@ -58,6 +77,9 @@ class DoubleSpinBox : public QDoubleSpinBox QSize minimumSizeHint() const override; QString textFromValue(double val) const override; + +protected: + void wheelEvent(QWheelEvent *event) override; }; /** diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 4c9f90d167..b720a17b54 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -496,14 +495,14 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) auto verticalLabel = new ElidingLabel(tr("Vertical"), editor); layout->addWidget(verticalLabel, 1, 0); - auto horizontalComboBox = new QComboBox(editor); + auto horizontalComboBox = new ComboBox(editor); horizontalComboBox->addItem(tr("Left"), Qt::AlignLeft); horizontalComboBox->addItem(tr("Center"), Qt::AlignHCenter); horizontalComboBox->addItem(tr("Right"), Qt::AlignRight); horizontalComboBox->addItem(tr("Justify"), Qt::AlignJustify); layout->addWidget(horizontalComboBox, 0, 1); - auto verticalComboBox = new QComboBox(editor); + auto verticalComboBox = new ComboBox(editor); verticalComboBox->addItem(tr("Top"), Qt::AlignTop); verticalComboBox->addItem(tr("Center"), Qt::AlignVCenter); verticalComboBox->addItem(tr("Bottom"), Qt::AlignBottom); @@ -792,9 +791,7 @@ void VariantEditor::updatePropertyActions(const PropertyWidgets &widgets, Proper QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { - auto editor = new QComboBox(parent); - // This allows the combo box to shrink horizontally. - editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + auto editor = new ComboBox(parent); for (qsizetype i = 0; i < m_enumData.names.size(); ++i) { auto value = m_enumData.values.value(i, i); From 6fc6c5d88afc38ca8aa14a6f50424f086b66a518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 25 Oct 2024 16:35:39 +0200 Subject: [PATCH 49/78] Fixed changing the compression level property It was affecting the background color instead, due to a wrong constructor overload getting chosen. --- src/tiled/propertieswidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 93a4618dcb..aede927301 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1006,7 +1006,9 @@ class MapProperties : public ObjectProperties return map()->compressionLevel(); }, [this](const int &value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapProperty(mapDocument(), + Map::CompressionLevelProperty, + value)); }); mBackgroundColorProperty = new ColorProperty( From dd0784a5fb5d88b457537c07f62f08f8fe693369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 25 Oct 2024 18:13:56 +0200 Subject: [PATCH 50/78] Addressed a few usability issues * Cursor no longer jumps to the end when editing string properties. * Global undo/redo shortcuts are no longer overridden by those local to property edit widgets. * Since global undo/redo is now relied upon, the keyboard tracking is enabled again for spin boxes. --- src/tiled/propertieswidget.cpp | 6 +++- src/tiled/propertyeditorwidgets.cpp | 53 +++++++++++++++++++++++++---- src/tiled/propertyeditorwidgets.h | 22 +++++++++++- src/tiled/varianteditor.cpp | 7 ++-- 4 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index aede927301..43fa0c6d16 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -763,7 +763,11 @@ class ClassNameProperty : public StringProperty editor->addItems(classNamesFor(*mObject)); auto syncEditor = [this, editor] { const QSignalBlocker blocker(editor); - editor->setCurrentText(value()); + + // Avoid affecting cursor position when the text is the same + const auto v = value(); + if (editor->currentText() != v) + editor->setCurrentText(v); }; syncEditor(); connect(this, &Property::valueChanged, editor, syncEditor); diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 9d86ae31df..c4255ccbbd 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -31,6 +31,20 @@ namespace Tiled { +/** + * Returns whether the given event is a shortcut override event for the undo or + * redo shortcuts. We generally want to use the global undo and redo shortcuts + * instead. + */ +static bool isUndoRedoShortcutOverride(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + auto ke = static_cast(event); + return (ke == QKeySequence::Redo || ke == QKeySequence::Undo); + } + return false; +} + /** * Strips a floating point number representation of redundant trailing zeros. * Examples: @@ -55,6 +69,15 @@ static QString removeRedundantTrialingZeros(const QString &text) } +bool LineEdit::event(QEvent *event) +{ + if (isUndoRedoShortcutOverride(event)) + return false; + + return QLineEdit::event(event); +} + + ComboBox::ComboBox(QWidget *parent) : QComboBox(parent) { @@ -66,6 +89,14 @@ ComboBox::ComboBox(QWidget *parent) setFocusPolicy(Qt::StrongFocus); } +bool ComboBox::event(QEvent *event) +{ + if (isUndoRedoShortcutOverride(event)) // relevant when editable + return false; + + return QComboBox::event(event); +} + void ComboBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) @@ -85,9 +116,6 @@ SpinBox::SpinBox(QWidget *parent) // Don't take focus by mouse wheel setFocusPolicy(Qt::StrongFocus); - // Don't respond to keyboard input immediately. - setKeyboardTracking(false); - // Allow the widget to shrink horizontally. setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } @@ -100,6 +128,14 @@ QSize SpinBox::minimumSizeHint() const return hint; } +bool SpinBox::event(QEvent *event) +{ + if (isUndoRedoShortcutOverride(event)) // relevant when editable + return false; + + return QSpinBox::event(event); +} + void SpinBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) @@ -122,9 +158,6 @@ DoubleSpinBox::DoubleSpinBox(QWidget *parent) // Don't take focus by mouse wheel setFocusPolicy(Qt::StrongFocus); - // Don't respond to keyboard input immediately. - setKeyboardTracking(false); - // Allow the widget to shrink horizontally. setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } @@ -148,6 +181,14 @@ QString DoubleSpinBox::textFromValue(double val) const return text; } +bool DoubleSpinBox::event(QEvent *event) +{ + if (isUndoRedoShortcutOverride(event)) // relevant when editable + return false; + + return QDoubleSpinBox::event(event); +} + void DoubleSpinBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index bd320bcf62..070faadf43 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -22,6 +22,7 @@ #include #include +#include #include class QLabel; @@ -29,7 +30,23 @@ class QLabel; namespace Tiled { /** - * A combo box that doesn't respond to wheel events when not focused. + * A line edit that doesn't override global undo/redo shortcuts. + */ +class LineEdit : public QLineEdit +{ + Q_OBJECT + +public: + using QLineEdit::QLineEdit; + +protected: + bool event(QEvent *event) override; +}; + + +/** + * A combo box that doesn't respond to wheel events when not focused and + * doesn't override global undo/redo shortcuts. */ class ComboBox : public QComboBox { @@ -39,6 +56,7 @@ class ComboBox : public QComboBox ComboBox(QWidget *parent = nullptr); protected: + bool event(QEvent *event) override; void wheelEvent(QWheelEvent *event) override; }; @@ -57,6 +75,7 @@ class SpinBox : public QSpinBox QSize minimumSizeHint() const override; protected: + bool event(QEvent *event) override; void wheelEvent(QWheelEvent *event) override; }; @@ -79,6 +98,7 @@ class DoubleSpinBox : public QDoubleSpinBox QString textFromValue(double val) const override; protected: + bool event(QEvent *event) override; void wheelEvent(QWheelEvent *event) override; }; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index b720a17b54..632c47496a 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -106,11 +106,14 @@ void StringProperty::setPlaceholderText(const QString &placeholderText) QWidget *StringProperty::createEditor(QWidget *parent) { - auto editor = new QLineEdit(parent); + auto editor = new LineEdit(parent); editor->setPlaceholderText(m_placeholderText); auto syncEditor = [=] { - editor->setText(value()); + // Avoid affecting cursor position when the text is the same + const QString v = value(); + if (editor->text() != v) + editor->setText(v); }; syncEditor(); From 2177fac48053757b45462c042fc2d98da9462006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 29 Oct 2024 08:57:05 +0100 Subject: [PATCH 51/78] Made consecutive edits to map properties merge together Like already happens for the properties of most other data types. The new properties view made it extra noticeable that each change became its own undo command. --- src/libtiled/map.h | 10 +- src/tiled/changelayer.cpp | 1 - src/tiled/changemapproperty.cpp | 426 +++++++++++++++++--------------- src/tiled/changemapproperty.h | 245 +++++++++++------- src/tiled/editablemap.cpp | 22 +- src/tiled/mapscene.cpp | 23 +- src/tiled/mapscene.h | 1 - src/tiled/propertieswidget.cpp | 67 ++--- src/tiled/propertybrowser.cpp | 37 ++- src/tiled/undocommands.h | 12 + 10 files changed, 455 insertions(+), 389 deletions(-) diff --git a/src/libtiled/map.h b/src/libtiled/map.h index b7e58117fd..f353ad72f8 100644 --- a/src/libtiled/map.h +++ b/src/libtiled/map.h @@ -78,8 +78,7 @@ class TILEDSHARED_EXPORT Map : public Object QString exportFormat; enum Property { - TileWidthProperty, - TileHeightProperty, + TileSizeProperty, InfiniteProperty, HexSideLengthProperty, StaggerAxisProperty, @@ -212,6 +211,7 @@ class TILEDSHARED_EXPORT Map : public Object void setTileHeight(int height); QSize tileSize() const; + void setTileSize(QSize size); bool infinite() const; void setInfinite(bool infinite); @@ -455,6 +455,12 @@ inline QSize Map::tileSize() const return QSize(mParameters.tileWidth, mParameters.tileHeight); } +inline void Map::setTileSize(QSize size) +{ + mParameters.tileWidth = size.width(); + mParameters.tileHeight = size.height(); +} + inline bool Map::infinite() const { return mParameters.infinite; diff --git a/src/tiled/changelayer.cpp b/src/tiled/changelayer.cpp index 93ce88f176..39427fb664 100644 --- a/src/tiled/changelayer.cpp +++ b/src/tiled/changelayer.cpp @@ -23,7 +23,6 @@ #include "changeevents.h" #include "document.h" #include "layer.h" -#include "map.h" #include diff --git a/src/tiled/changemapproperty.cpp b/src/tiled/changemapproperty.cpp index c52d4052d0..6a03a2fc0f 100644 --- a/src/tiled/changemapproperty.cpp +++ b/src/tiled/changemapproperty.cpp @@ -21,221 +21,235 @@ #include "changemapproperty.h" #include "changeevents.h" -#include "map.h" #include "mapdocument.h" #include using namespace Tiled; -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::Property property, - int value) - : mMapDocument(mapDocument) - , mProperty(property) - , mIntValue(value) -{ - switch (property) { - case Map::TileWidthProperty: - setText(QCoreApplication::translate("Undo Commands", - "Change Tile Width")); - break; - case Map::TileHeightProperty: - setText(QCoreApplication::translate("Undo Commands", - "Change Tile Height")); - break; - case Map::InfiniteProperty: - setText(QCoreApplication::translate("Undo Commands", - "Change Infinite Property")); - break; - case Map::HexSideLengthProperty: - setText(QCoreApplication::translate("Undo Commands", - "Change Hex Side Length")); - break; - case Map::CompressionLevelProperty: - setText(QCoreApplication::translate("Undo Commands", - "Change Compression Level")); - break; - default: - break; - } +ChangeMapBackgroundColor::ChangeMapBackgroundColor(MapDocument *document, const QColor &backgroundColor) + : ChangeValue(document, { document->map() }, backgroundColor) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Background Color")); +} + +QColor ChangeMapBackgroundColor::getValue(const Map *map) const +{ + return map->backgroundColor(); +} + +void ChangeMapBackgroundColor::setValue(Map *map, const QColor &value) const +{ + map->setBackgroundColor(value); + emit document()->changed(MapChangeEvent(Map::BackgroundColorProperty)); +} + + +ChangeMapChunkSize::ChangeMapChunkSize(MapDocument *document, const QSize &chunkSize) + : ChangeValue(document, { document->map() }, chunkSize) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Chunk Size")); +} + +QSize ChangeMapChunkSize::getValue(const Map *map) const +{ + return map->chunkSize(); +} + +void ChangeMapChunkSize::setValue(Map *map, const QSize &value) const +{ + map->setChunkSize(value); + emit document()->changed(MapChangeEvent(Map::ChunkSizeProperty)); +} + + +ChangeMapStaggerAxis::ChangeMapStaggerAxis(MapDocument *document, Map::StaggerAxis staggerAxis) + : ChangeValue(document, { document->map() }, staggerAxis) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Stagger Axis")); +} + +Map::StaggerAxis ChangeMapStaggerAxis::getValue(const Map *map) const +{ + return map->staggerAxis(); +} + +void ChangeMapStaggerAxis::setValue(Map *map, const Map::StaggerAxis &value) const +{ + map->setStaggerAxis(value); + emit document()->changed(MapChangeEvent(Map::StaggerAxisProperty)); +} + + +ChangeMapStaggerIndex::ChangeMapStaggerIndex(MapDocument *document, Map::StaggerIndex staggerIndex) + : ChangeValue(document, { document->map() }, staggerIndex) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Stagger Index")); +} + +Map::StaggerIndex ChangeMapStaggerIndex::getValue(const Map *map) const +{ + return map->staggerIndex(); +} + +void ChangeMapStaggerIndex::setValue(Map *map, const Map::StaggerIndex &value) const +{ + map->setStaggerIndex(value); + emit document()->changed(MapChangeEvent(Map::StaggerIndexProperty)); +} + + +ChangeMapParallaxOrigin::ChangeMapParallaxOrigin(MapDocument *document, const QPointF ¶llaxOrigin) + : ChangeValue(document, { document->map() }, parallaxOrigin) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Parallax Origin")); +} + +QPointF ChangeMapParallaxOrigin::getValue(const Map *map) const +{ + return map->parallaxOrigin(); +} + +void ChangeMapParallaxOrigin::setValue(Map *map, const QPointF &value) const +{ + map->setParallaxOrigin(value); + emit document()->changed(MapChangeEvent(Map::ParallaxOriginProperty)); +} + + +ChangeMapOrientation::ChangeMapOrientation(MapDocument *document, Map::Orientation orientation) + : ChangeValue(document, { document->map() }, orientation) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Orientation")); +} + +Map::Orientation ChangeMapOrientation::getValue(const Map *map) const +{ + return map->orientation(); +} + +void ChangeMapOrientation::setValue(Map *map, const Map::Orientation &value) const +{ + map->setOrientation(value); + emit document()->changed(MapChangeEvent(Map::OrientationProperty)); +} + + +ChangeMapRenderOrder::ChangeMapRenderOrder(MapDocument *document, Map::RenderOrder renderOrder) + : ChangeValue(document, { document->map() }, renderOrder) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Render Order")); +} + +Map::RenderOrder ChangeMapRenderOrder::getValue(const Map *map) const +{ + return map->renderOrder(); +} + +void ChangeMapRenderOrder::setValue(Map *map, const Map::RenderOrder &value) const +{ + map->setRenderOrder(value); + emit document()->changed(MapChangeEvent(Map::RenderOrderProperty)); +} + + +ChangeMapLayerDataFormat::ChangeMapLayerDataFormat(MapDocument *document, Map::LayerDataFormat layerDataFormat) + : ChangeValue(document, { document->map() }, layerDataFormat) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Layer Data Format")); +} + +Map::LayerDataFormat ChangeMapLayerDataFormat::getValue(const Map *map) const +{ + return map->layerDataFormat(); +} + +void ChangeMapLayerDataFormat::setValue(Map *map, const Map::LayerDataFormat &value) const +{ + map->setLayerDataFormat(value); + emit document()->changed(MapChangeEvent(Map::LayerDataFormatProperty)); +} + + +ChangeMapTileSize::ChangeMapTileSize(MapDocument *document, const QSize &size) + : ChangeValue(document, { document->map() }, size) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Tile Size")); +} + +QSize ChangeMapTileSize::getValue(const Map *map) const +{ + return map->tileSize(); } -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - const QColor &backgroundColor) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Background Color")) - , mMapDocument(mapDocument) - , mProperty(Map::BackgroundColorProperty) - , mBackgroundColor(backgroundColor) +void ChangeMapTileSize::setValue(Map *map, const QSize &value) const { + map->setTileSize(value); + emit document()->changed(MapChangeEvent(Map::TileSizeProperty)); } -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - QSize chunkSize) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Chunk Size")) - , mMapDocument(mapDocument) - , mProperty(Map::ChunkSizeProperty) - , mChunkSize(chunkSize) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::StaggerAxis staggerAxis) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Stagger Axis")) - , mMapDocument(mapDocument) - , mProperty(Map::StaggerAxisProperty) - , mStaggerAxis(staggerAxis) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::StaggerIndex staggerIndex) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Stagger Index")) - , mMapDocument(mapDocument) - , mProperty(Map::StaggerIndexProperty) - , mStaggerIndex(staggerIndex) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - const QPointF ¶llaxOrigin) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Parallax Origin")) - , mMapDocument(mapDocument) - , mProperty(Map::ParallaxOriginProperty) - , mParallaxOrigin(parallaxOrigin) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::Orientation orientation) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Orientation")) - , mMapDocument(mapDocument) - , mProperty(Map::OrientationProperty) - , mOrientation(orientation) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::RenderOrder renderOrder) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Render Order")) - , mMapDocument(mapDocument) - , mProperty(Map::RenderOrderProperty) - , mRenderOrder(renderOrder) -{ -} - -ChangeMapProperty::ChangeMapProperty(MapDocument *mapDocument, - Map::LayerDataFormat layerDataFormat) - : QUndoCommand(QCoreApplication::translate("Undo Commands", - "Change Layer Data Format")) - , mMapDocument(mapDocument) - , mProperty(Map::LayerDataFormatProperty) - , mLayerDataFormat(layerDataFormat) -{ -} - -void ChangeMapProperty::redo() -{ - swap(); -} - -void ChangeMapProperty::undo() -{ - swap(); -} - -void ChangeMapProperty::swap() -{ - Map *map = mMapDocument->map(); - - switch (mProperty) { - case Map::TileWidthProperty: { - const int tileWidth = map->tileWidth(); - map->setTileWidth(mIntValue); - mIntValue = tileWidth; - break; - } - case Map::TileHeightProperty: { - const int tileHeight = map->tileHeight(); - map->setTileHeight(mIntValue); - mIntValue = tileHeight; - break; - } - case Map::InfiniteProperty: { - const int infinite = map->infinite(); - map->setInfinite(mIntValue); - mIntValue = infinite; - break; - } - case Map::OrientationProperty: { - const Map::Orientation orientation = map->orientation(); - map->setOrientation(mOrientation); - mOrientation = orientation; - break; - } - case Map::HexSideLengthProperty: { - const int hexSideLength = map->hexSideLength(); - map->setHexSideLength(mIntValue); - mIntValue = hexSideLength; - break; - } - case Map::StaggerAxisProperty: { - const Map::StaggerAxis staggerAxis = map->staggerAxis(); - map->setStaggerAxis(mStaggerAxis); - mStaggerAxis = staggerAxis; - break; - } - case Map::StaggerIndexProperty: { - const Map::StaggerIndex staggerIndex = map->staggerIndex(); - map->setStaggerIndex(mStaggerIndex); - mStaggerIndex = staggerIndex; - break; - } - case Map::ParallaxOriginProperty: { - const QPointF parallaxOrigin = map->parallaxOrigin(); - map->setParallaxOrigin(mParallaxOrigin); - mParallaxOrigin = parallaxOrigin; - break; - } - case Map::RenderOrderProperty: { - const Map::RenderOrder renderOrder = map->renderOrder(); - map->setRenderOrder(mRenderOrder); - mRenderOrder = renderOrder; - break; - } - case Map::BackgroundColorProperty: { - const QColor backgroundColor = map->backgroundColor(); - map->setBackgroundColor(mBackgroundColor); - mBackgroundColor = backgroundColor; - break; - } - case Map::LayerDataFormatProperty: { - const Map::LayerDataFormat layerDataFormat = map->layerDataFormat(); - map->setLayerDataFormat(mLayerDataFormat); - mLayerDataFormat = layerDataFormat; - break; - } - case Map::CompressionLevelProperty: { - const int compressionLevel = map->compressionLevel(); - map->setCompressionLevel(mIntValue); - mIntValue = compressionLevel; - break; - } - case Map::ChunkSizeProperty: { - const QSize chunkSize = map->chunkSize(); - map->setChunkSize(mChunkSize); - mChunkSize = chunkSize; - break; - } - } - - emit mMapDocument->changed(MapChangeEvent(mProperty)); - emit mMapDocument->mapChanged(); + +ChangeMapInfinite::ChangeMapInfinite(MapDocument *document, bool infinite) + : ChangeValue(document, { document->map() }, infinite) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Infinite Property")); +} + +bool ChangeMapInfinite::getValue(const Map *map) const +{ + return map->infinite(); +} + +void ChangeMapInfinite::setValue(Map *map, const bool &value) const +{ + map->setInfinite(value); + emit document()->changed(MapChangeEvent(Map::InfiniteProperty)); +} + + +ChangeMapHexSideLength::ChangeMapHexSideLength(MapDocument *document, int hexSideLength) + : ChangeValue(document, { document->map() }, hexSideLength) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Hex Side Length")); +} + +int ChangeMapHexSideLength::getValue(const Map *map) const +{ + return map->hexSideLength(); +} + +void ChangeMapHexSideLength::setValue(Map *map, const int &value) const +{ + map->setHexSideLength(value); + emit document()->changed(MapChangeEvent(Map::HexSideLengthProperty)); +} + + +ChangeMapCompressionLevel::ChangeMapCompressionLevel(MapDocument *document, int compressionLevel) + : ChangeValue(document, { document->map() }, compressionLevel) +{ + setText(QCoreApplication::translate("Undo Commands", + "Change Compression Level")); +} + +int ChangeMapCompressionLevel::getValue(const Map *map) const +{ + return map->compressionLevel(); +} + +void ChangeMapCompressionLevel::setValue(Map *map, const int &value) const +{ + map->setCompressionLevel(value); + emit document()->changed(MapChangeEvent(Map::CompressionLevelProperty)); } diff --git a/src/tiled/changemapproperty.h b/src/tiled/changemapproperty.h index 03e93dd403..7c87becebf 100644 --- a/src/tiled/changemapproperty.h +++ b/src/tiled/changemapproperty.h @@ -20,111 +20,170 @@ #pragma once +#include "changeevents.h" +#include "changevalue.h" #include "map.h" +#include "mapdocument.h" #include -#include namespace Tiled { class MapDocument; -class ChangeMapProperty : public QUndoCommand +class ChangeMapBackgroundColor : public ChangeValue { public: - /** - * Constructs a command that changes the value of the given property. - * - * Can only be used for the HexSideLength property. - * - * @param mapDocument the map document of the map - * @param backgroundColor the new color to apply for the background - */ - ChangeMapProperty(MapDocument *mapDocument, Map::Property property, int value); - - /** - * Constructs a command that changes the map background color. - * - * @param mapDocument the map document of the map - * @param backgroundColor the new color to apply for the background - */ - ChangeMapProperty(MapDocument *mapDocument, const QColor &backgroundColor); - - /** - * Constructs a command that changes the chunk size. - * - * @param mapDocument the map document of the map - * @param chunkSize the new chunk size to use for tile layers - */ - ChangeMapProperty(MapDocument *mapDocument, QSize chunkSize); - - /** - * Constructs a command that changes the map stagger axis. - * - * @param mapDocument the map document of the map - * @param staggerAxis the new map stagger axis - */ - ChangeMapProperty(MapDocument *mapDocument, Map::StaggerAxis staggerAxis); - - /** - * Constructs a command that changes the map stagger index. - * - * @param mapDocument the map document of the map - * @param staggerIndex the new map stagger index - */ - ChangeMapProperty(MapDocument *mapDocument, Map::StaggerIndex staggerIndex); - - /** - * Constructs a command that changes the parallax origin. - * - * @param mapDocument the map document of the map - * @param parallaxOrigin the new parallax origin - */ - ChangeMapProperty(MapDocument *mapDocument, const QPointF ¶llaxOrigin); - - /** - * Constructs a command that changes the map orientation. - * - * @param mapDocument the map document of the map - * @param orientation the new map orientation - */ - ChangeMapProperty(MapDocument *mapDocument, Map::Orientation orientation); - - /** - * Constructs a command that changes the render order. - * - * @param mapDocument the map document of the map - * @param renderOrder the new map render order - */ - ChangeMapProperty(MapDocument *mapDocument, Map::RenderOrder renderOrder); - - /** - * Constructs a command that changes the layer data format. - * - * @param mapDocument the map document of the map - * @param layerDataFormat the new layer data format - */ - ChangeMapProperty(MapDocument *mapDocument, Map::LayerDataFormat layerDataFormat); - - void undo() override; - void redo() override; + ChangeMapBackgroundColor(MapDocument *document, const QColor &backgroundColor); + + int id() const override { return Cmd_ChangeMapBackgroundColor; } + +private: + QColor getValue(const Map *map) const override; + void setValue(Map *map, const QColor &value) const override; +}; + + +class ChangeMapChunkSize : public ChangeValue +{ +public: + ChangeMapChunkSize(MapDocument *document, const QSize &chunkSize); + + int id() const override { return Cmd_ChangeMapChunkSize; } + +private: + QSize getValue(const Map *map) const override; + void setValue(Map *map, const QSize &value) const override; +}; + + +class ChangeMapStaggerAxis : public ChangeValue +{ +public: + ChangeMapStaggerAxis(MapDocument *document, Map::StaggerAxis staggerAxis); + + int id() const override { return Cmd_ChangeMapStaggerAxis; } + +private: + Map::StaggerAxis getValue(const Map *map) const override; + void setValue(Map *map, const Map::StaggerAxis &value) const override; +}; + + +class ChangeMapStaggerIndex : public ChangeValue +{ +public: + ChangeMapStaggerIndex(MapDocument *document, Map::StaggerIndex staggerIndex); + + int id() const override { return Cmd_ChangeMapStaggerIndex; } + +private: + Map::StaggerIndex getValue(const Map *map) const override; + void setValue(Map *map, const Map::StaggerIndex &value) const override; +}; + + +class ChangeMapParallaxOrigin : public ChangeValue +{ +public: + ChangeMapParallaxOrigin(MapDocument *document, const QPointF ¶llaxOrigin); + + int id() const override { return Cmd_ChangeMapParallaxOrigin; } + +private: + QPointF getValue(const Map *map) const override; + void setValue(Map *map, const QPointF &value) const override; +}; + + +class ChangeMapOrientation : public ChangeValue +{ +public: + ChangeMapOrientation(MapDocument *document, Map::Orientation orientation); + + int id() const override { return Cmd_ChangeMapOrientation; } + +private: + Map::Orientation getValue(const Map *map) const override; + void setValue(Map *map, const Map::Orientation &value) const override; +}; + + +class ChangeMapRenderOrder : public ChangeValue +{ +public: + ChangeMapRenderOrder(MapDocument *document, Map::RenderOrder renderOrder); + + int id() const override { return Cmd_ChangeMapRenderOrder; } + +private: + Map::RenderOrder getValue(const Map *map) const override; + void setValue(Map *map, const Map::RenderOrder &value) const override; +}; + + +class ChangeMapLayerDataFormat : public ChangeValue +{ +public: + ChangeMapLayerDataFormat(MapDocument *document, Map::LayerDataFormat layerDataFormat); + + int id() const override { return Cmd_ChangeMapLayerDataFormat; } + +private: + Map::LayerDataFormat getValue(const Map *map) const override; + void setValue(Map *map, const Map::LayerDataFormat &value) const override; +}; + + +class ChangeMapTileSize : public ChangeValue +{ +public: + ChangeMapTileSize(MapDocument *document, const QSize &tileSize); + + int id() const override { return Cmd_ChangeMapTileSize; } + +private: + QSize getValue(const Map *map) const override; + void setValue(Map *map, const QSize &value) const override; +}; + + +class ChangeMapInfinite : public ChangeValue +{ +public: + ChangeMapInfinite(MapDocument *document, bool infinite); + + int id() const override { return Cmd_ChangeMapInfinite; } + +private: + bool getValue(const Map *map) const override; + void setValue(Map *map, const bool &value) const override; +}; + + +class ChangeMapHexSideLength : public ChangeValue +{ +public: + ChangeMapHexSideLength(MapDocument *document, int hexSideLength); + + int id() const override { return Cmd_ChangeMapHexSideLength; } + +private: + int getValue(const Map *map) const override; + void setValue(Map *map, const int &value) const override; +}; + + +class ChangeMapCompressionLevel : public ChangeValue +{ +public: + ChangeMapCompressionLevel(MapDocument *document, int compressionLevel); + + int id() const override { return Cmd_ChangeMapCompressionLevel; } private: - void swap(); - - MapDocument *mMapDocument; - Map::Property mProperty; - QColor mBackgroundColor; - QSize mChunkSize; - union { - int mIntValue; - Map::StaggerAxis mStaggerAxis; - Map::StaggerIndex mStaggerIndex; - QPointF mParallaxOrigin; - Map::Orientation mOrientation; - Map::RenderOrder mRenderOrder; - Map::LayerDataFormat mLayerDataFormat; - }; + int getValue(const Map *map) const override; + void setValue(Map *map, const int &value) const override; }; } // namespace Tiled diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index d0f3a2878a..02c0b97572 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -495,7 +495,7 @@ void EditableMap::setSize(int width, int height) void EditableMap::setTileWidth(int value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, Map::TileWidthProperty, value)); + push(new ChangeMapTileSize(doc, QSize(value, tileHeight()))); else if (!checkReadOnly()) map()->setTileWidth(value); } @@ -503,7 +503,7 @@ void EditableMap::setTileWidth(int value) void EditableMap::setTileHeight(int value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, Map::TileHeightProperty, value)); + push(new ChangeMapTileSize(doc, QSize(tileWidth(), value))); else if (!checkReadOnly()) map()->setTileHeight(value); } @@ -528,7 +528,7 @@ void EditableMap::setTileSize(int width, int height) void EditableMap::setInfinite(bool value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, Map::InfiniteProperty, value)); + push(new ChangeMapInfinite(doc, value)); else if (!checkReadOnly()) map()->setInfinite(value); } @@ -536,7 +536,7 @@ void EditableMap::setInfinite(bool value) void EditableMap::setHexSideLength(int value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, Map::HexSideLengthProperty, value)); + push(new ChangeMapHexSideLength(doc, value)); else if (!checkReadOnly()) map()->setHexSideLength(value); } @@ -544,7 +544,7 @@ void EditableMap::setHexSideLength(int value) void EditableMap::setStaggerAxis(StaggerAxis value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, static_cast(value))); + push(new ChangeMapStaggerAxis(doc, static_cast(value))); else if (!checkReadOnly()) map()->setStaggerAxis(static_cast(value)); } @@ -552,7 +552,7 @@ void EditableMap::setStaggerAxis(StaggerAxis value) void EditableMap::setStaggerIndex(StaggerIndex value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, static_cast(value))); + push(new ChangeMapStaggerIndex(doc, static_cast(value))); else if (!checkReadOnly()) map()->setStaggerIndex(static_cast(value)); } @@ -560,7 +560,7 @@ void EditableMap::setStaggerIndex(StaggerIndex value) void EditableMap::setParallaxOrigin(const QPointF ¶llaxOrigin) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, parallaxOrigin)); + push(new ChangeMapParallaxOrigin(doc, parallaxOrigin)); else if (!checkReadOnly()) map()->setParallaxOrigin(parallaxOrigin); } @@ -568,7 +568,7 @@ void EditableMap::setParallaxOrigin(const QPointF ¶llaxOrigin) void EditableMap::setOrientation(Orientation value) { if (auto doc = mapDocument()) { - push(new ChangeMapProperty(doc, static_cast(value))); + push(new ChangeMapOrientation(doc, static_cast(value))); } else if (!checkReadOnly()) { map()->setOrientation(static_cast(value)); mRenderer.reset(); @@ -578,7 +578,7 @@ void EditableMap::setOrientation(Orientation value) void EditableMap::setRenderOrder(RenderOrder value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, static_cast(value))); + push(new ChangeMapRenderOrder(doc, static_cast(value))); else if (!checkReadOnly()) map()->setRenderOrder(static_cast(value)); } @@ -586,7 +586,7 @@ void EditableMap::setRenderOrder(RenderOrder value) void EditableMap::setBackgroundColor(const QColor &value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, value)); + push(new ChangeMapBackgroundColor(doc, value)); else if (!checkReadOnly()) map()->setBackgroundColor(value); } @@ -594,7 +594,7 @@ void EditableMap::setBackgroundColor(const QColor &value) void EditableMap::setLayerDataFormat(LayerDataFormat value) { if (auto doc = mapDocument()) - push(new ChangeMapProperty(doc, static_cast(value))); + push(new ChangeMapLayerDataFormat(doc, static_cast(value))); else if (!checkReadOnly()) map()->setLayerDataFormat(static_cast(value)); } diff --git a/src/tiled/mapscene.cpp b/src/tiled/mapscene.cpp index c87faf96b2..d9609b1d80 100644 --- a/src/tiled/mapscene.cpp +++ b/src/tiled/mapscene.cpp @@ -102,8 +102,6 @@ void MapScene::setMapDocument(MapDocument *mapDocument) if (mMapDocument) { connect(mMapDocument, &MapDocument::changed, this, &MapScene::changeEvent); - connect(mMapDocument, &MapDocument::mapChanged, - this, &MapScene::mapChanged); connect(mMapDocument, &MapDocument::tilesetTilePositioningChanged, this, [this] { update(); }); connect(mMapDocument, &MapDocument::tileImageSourceChanged, @@ -405,10 +403,19 @@ MapItem *MapScene::takeOrCreateMapItem(const MapDocumentPtr &mapDocument, MapIte void MapScene::changeEvent(const ChangeEvent &change) { switch (change.type) { - case ChangeEvent::MapChanged: - if (static_cast(change).property == Map::ParallaxOriginProperty) + case ChangeEvent::MapChanged: { + switch (static_cast(change).property) { + case Map::ParallaxOriginProperty: emit parallaxParametersChanged(); + break; + case Map::BackgroundColorProperty: + updateBackgroundColor(); + break; + default: + break; + } break; + } case ChangeEvent::TilesetChanged:{ auto &tilesetChange = static_cast(change); switch (tilesetChange.property) { @@ -424,14 +431,6 @@ void MapScene::changeEvent(const ChangeEvent &change) } } -/** - * Updates the possibly changed background color. - */ -void MapScene::mapChanged() -{ - updateBackgroundColor(); -} - void MapScene::repaintTileset(Tileset *tileset) { for (MapItem *mapItem : std::as_const(mMapItems)) { diff --git a/src/tiled/mapscene.h b/src/tiled/mapscene.h index 7eacf13290..3e81127b9d 100644 --- a/src/tiled/mapscene.h +++ b/src/tiled/mapscene.h @@ -112,7 +112,6 @@ class MapScene : public QGraphicsScene void refreshScene(); void changeEvent(const ChangeEvent &change); - void mapChanged(); void repaintTileset(Tileset *tileset); void tilesetReplaced(int index, Tileset *tileset, Tileset *oldTileset); diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 43fa0c6d16..4ec7c3d11e 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -896,7 +896,7 @@ class MapProperties : public ObjectProperties return map()->orientation(); }, [this](Map::Orientation value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapOrientation(mapDocument(), value)); }); mSizeProperty = new MapSizeProperty(mapDocument(), this); @@ -907,19 +907,7 @@ class MapProperties : public ObjectProperties return mapDocument()->map()->tileSize(); }, [this](const QSize &newSize) { - const auto oldSize = mapDocument()->map()->tileSize(); - - if (oldSize.width() != newSize.width()) { - push(new ChangeMapProperty(mapDocument(), - Map::TileWidthProperty, - newSize.width())); - } - - if (oldSize.height() != newSize.height()) { - push(new ChangeMapProperty(mapDocument(), - Map::TileHeightProperty, - newSize.height())); - } + push(new ChangeMapTileSize(mapDocument(), newSize)); }, this); mTileSizeProperty->setMinimum(1); @@ -931,9 +919,7 @@ class MapProperties : public ObjectProperties return map()->infinite(); }, [this](const bool &value) { - push(new ChangeMapProperty(mapDocument(), - Map::InfiniteProperty, - value ? 1 : 0)); + push(new ChangeMapInfinite(mapDocument(), value)); }); mInfiniteProperty->setNameOnCheckBox(true); @@ -942,10 +928,8 @@ class MapProperties : public ObjectProperties [this] { return map()->hexSideLength(); }, - [this](const QVariant &value) { - push(new ChangeMapProperty(mapDocument(), - Map::HexSideLengthProperty, - value.toInt())); + [this](const int &value) { + push(new ChangeMapHexSideLength(mapDocument(), value)); }); mHexSideLengthProperty->setSuffix(tr(" px")); @@ -955,7 +939,7 @@ class MapProperties : public ObjectProperties return map()->staggerAxis(); }, [this](Map::StaggerAxis value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapStaggerAxis(mapDocument(), value)); }); mStaggerIndexProperty = new EnumProperty( @@ -964,7 +948,7 @@ class MapProperties : public ObjectProperties return map()->staggerIndex(); }, [this](Map::StaggerIndex value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapStaggerIndex(mapDocument(), value)); }); mParallaxOriginProperty = new PointFProperty( @@ -973,7 +957,7 @@ class MapProperties : public ObjectProperties return map()->parallaxOrigin(); }, [this](const QPointF &value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapParallaxOrigin(mapDocument(), value)); }); mLayerDataFormatProperty = new EnumProperty( @@ -982,7 +966,16 @@ class MapProperties : public ObjectProperties return map()->layerDataFormat(); }, [this](Map::LayerDataFormat value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapLayerDataFormat(mapDocument(), value)); + }); + + mCompressionLevelProperty = new IntProperty( + tr("Compression Level"), + [this] { + return map()->compressionLevel(); + }, + [this](const int &value) { + push(new ChangeMapCompressionLevel(mapDocument(), value)); }); mChunkSizeProperty = new SizeProperty( @@ -991,7 +984,7 @@ class MapProperties : public ObjectProperties return map()->chunkSize(); }, [this](const QSize &value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapChunkSize(mapDocument(), value)); }); mChunkSizeProperty->setMinimum(CHUNK_SIZE_MIN); @@ -1001,18 +994,7 @@ class MapProperties : public ObjectProperties return map()->renderOrder(); }, [this](Map::RenderOrder value) { - push(new ChangeMapProperty(mapDocument(), value)); - }); - - mCompressionLevelProperty = new IntProperty( - tr("Compression Level"), - [this] { - return map()->compressionLevel(); - }, - [this](const int &value) { - push(new ChangeMapProperty(mapDocument(), - Map::CompressionLevelProperty, - value)); + push(new ChangeMapRenderOrder(mapDocument(), value)); }); mBackgroundColorProperty = new ColorProperty( @@ -1021,7 +1003,7 @@ class MapProperties : public ObjectProperties return map()->backgroundColor(); }, [this](const QColor &value) { - push(new ChangeMapProperty(mapDocument(), value)); + push(new ChangeMapBackgroundColor(mapDocument(), value)); }); mMapProperties = new GroupProperty(tr("Map")); @@ -1038,8 +1020,8 @@ class MapProperties : public ObjectProperties mMapProperties->addProperty(mParallaxOriginProperty); mMapProperties->addSeparator(); mMapProperties->addProperty(mLayerDataFormatProperty); - mMapProperties->addProperty(mChunkSizeProperty); mMapProperties->addProperty(mCompressionLevelProperty); + mMapProperties->addProperty(mChunkSizeProperty); mMapProperties->addSeparator(); mMapProperties->addProperty(mRenderOrderProperty); mMapProperties->addProperty(mBackgroundColorProperty); @@ -1062,8 +1044,7 @@ class MapProperties : public ObjectProperties const auto property = static_cast(event).property; switch (property) { - case Map::TileWidthProperty: - case Map::TileHeightProperty: + case Map::TileSizeProperty: emit mTileSizeProperty->valueChanged(); break; case Map::InfiniteProperty: @@ -1149,9 +1130,9 @@ class MapProperties : public ObjectProperties Property *mStaggerIndexProperty; Property *mParallaxOriginProperty; Property *mLayerDataFormatProperty; + Property *mCompressionLevelProperty; SizeProperty *mChunkSizeProperty; Property *mRenderOrderProperty; - Property *mCompressionLevelProperty; Property *mBackgroundColorProperty; }; diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp index 511a33eee1..13f31df60b 100644 --- a/src/tiled/propertybrowser.cpp +++ b/src/tiled/propertybrowser.cpp @@ -1147,18 +1147,16 @@ void PropertyBrowser::applyMapValue(PropertyId id, const QVariant &val) switch (id) { case TileWidthProperty: - command = new ChangeMapProperty(mMapDocument, Map::TileWidthProperty, - val.toInt()); + command = new ChangeMapTileSize(mMapDocument, QSize(val.toInt(), + mMapDocument->map()->tileHeight())); break; case TileHeightProperty: - command = new ChangeMapProperty(mMapDocument, Map::TileHeightProperty, - val.toInt()); + command = new ChangeMapTileSize(mMapDocument, QSize(mMapDocument->map()->tileWidth(), + val.toInt())); break; case InfiniteProperty: { - bool infinite = val.toInt(); - - auto changePropertyCommand = new ChangeMapProperty(mMapDocument, Map::InfiniteProperty, - val.toInt()); + const bool infinite = val.toInt(); + auto changePropertyCommand = new ChangeMapInfinite(mMapDocument, infinite); QUndoStack *undoStack = mDocument->undoStack(); undoStack->beginMacro(changePropertyCommand->text()); @@ -1184,54 +1182,53 @@ void PropertyBrowser::applyMapValue(PropertyId id, const QVariant &val) } case OrientationProperty: { Map::Orientation orientation = static_cast(val.toInt() + 1); - command = new ChangeMapProperty(mMapDocument, orientation); + command = new ChangeMapOrientation(mMapDocument, orientation); break; } case HexSideLengthProperty: { - command = new ChangeMapProperty(mMapDocument, Map::HexSideLengthProperty, - val.toInt()); + command = new ChangeMapHexSideLength(mMapDocument, val.toInt()); break; } case StaggerAxisProperty: { Map::StaggerAxis staggerAxis = static_cast(val.toInt()); - command = new ChangeMapProperty(mMapDocument, staggerAxis); + command = new ChangeMapStaggerAxis(mMapDocument, staggerAxis); break; } case StaggerIndexProperty: { Map::StaggerIndex staggerIndex = static_cast(val.toInt()); - command = new ChangeMapProperty(mMapDocument, staggerIndex); + command = new ChangeMapStaggerIndex(mMapDocument, staggerIndex); break; } case ParallaxOriginProperty: { - command = new ChangeMapProperty(mMapDocument, val.value()); + command = new ChangeMapParallaxOrigin(mMapDocument, val.value()); break; } case LayerFormatProperty: { Map::LayerDataFormat format = mLayerFormatValues.at(val.toInt()); - command = new ChangeMapProperty(mMapDocument, format); + command = new ChangeMapLayerDataFormat(mMapDocument, format); break; } case RenderOrderProperty: { Map::RenderOrder renderOrder = static_cast(val.toInt()); - command = new ChangeMapProperty(mMapDocument, renderOrder); + command = new ChangeMapRenderOrder(mMapDocument, renderOrder); break; } case BackgroundColorProperty: - command = new ChangeMapProperty(mMapDocument, val.value()); + command = new ChangeMapBackgroundColor(mMapDocument, val.value()); break; case CompressionLevelProperty: - command = new ChangeMapProperty(mMapDocument, Map::CompressionLevelProperty, val.toInt()); + command = new ChangeMapCompressionLevel(mMapDocument, val.toInt()); break; case ChunkWidthProperty: { QSize chunkSize = mMapDocument->map()->chunkSize(); chunkSize.setWidth(val.toInt()); - command = new ChangeMapProperty(mMapDocument, chunkSize); + command = new ChangeMapChunkSize(mMapDocument, chunkSize); break; } case ChunkHeightProperty: { QSize chunkSize = mMapDocument->map()->chunkSize(); chunkSize.setHeight(val.toInt()); - command = new ChangeMapProperty(mMapDocument, chunkSize); + command = new ChangeMapChunkSize(mMapDocument, chunkSize); break; } default: diff --git a/src/tiled/undocommands.h b/src/tiled/undocommands.h index 3af2985d35..2627662510 100644 --- a/src/tiled/undocommands.h +++ b/src/tiled/undocommands.h @@ -39,8 +39,20 @@ enum UndoCommands { Cmd_ChangeLayerParallaxFactor, Cmd_ChangeLayerTintColor, Cmd_ChangeLayerVisible, + Cmd_ChangeMapBackgroundColor, + Cmd_ChangeMapChunkSize, + Cmd_ChangeMapCompressionLevel, + Cmd_ChangeMapHexSideLength, + Cmd_ChangeMapInfinite, + Cmd_ChangeMapLayerDataFormat, Cmd_ChangeMapObject, Cmd_ChangeMapObjectTransform, + Cmd_ChangeMapOrientation, + Cmd_ChangeMapParallaxOrigin, + Cmd_ChangeMapRenderOrder, + Cmd_ChangeMapStaggerAxis, + Cmd_ChangeMapStaggerIndex, + Cmd_ChangeMapTileSize, Cmd_ChangeSelectedArea, Cmd_ChangeTileImageRect, Cmd_ChangeTileProbability, From 82d7b4964af817d9e88eff0ab66f6e6074fb60ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 29 Oct 2024 09:15:19 +0100 Subject: [PATCH 52/78] Used ChangeMapProperty template to reduce code duplication --- src/tiled/changemapproperty.cpp | 255 ------------------------------ src/tiled/changemapproperty.h | 269 ++++++++++++++++++-------------- src/tiled/libtilededitor.qbs | 1 - 3 files changed, 156 insertions(+), 369 deletions(-) delete mode 100644 src/tiled/changemapproperty.cpp diff --git a/src/tiled/changemapproperty.cpp b/src/tiled/changemapproperty.cpp deleted file mode 100644 index 6a03a2fc0f..0000000000 --- a/src/tiled/changemapproperty.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * changemapproperty.cpp - * Copyright 2012, Emmanuel Barroga emmanuelbarroga@gmail.com - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "changemapproperty.h" - -#include "changeevents.h" -#include "mapdocument.h" - -#include - -using namespace Tiled; - -ChangeMapBackgroundColor::ChangeMapBackgroundColor(MapDocument *document, const QColor &backgroundColor) - : ChangeValue(document, { document->map() }, backgroundColor) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Background Color")); -} - -QColor ChangeMapBackgroundColor::getValue(const Map *map) const -{ - return map->backgroundColor(); -} - -void ChangeMapBackgroundColor::setValue(Map *map, const QColor &value) const -{ - map->setBackgroundColor(value); - emit document()->changed(MapChangeEvent(Map::BackgroundColorProperty)); -} - - -ChangeMapChunkSize::ChangeMapChunkSize(MapDocument *document, const QSize &chunkSize) - : ChangeValue(document, { document->map() }, chunkSize) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Chunk Size")); -} - -QSize ChangeMapChunkSize::getValue(const Map *map) const -{ - return map->chunkSize(); -} - -void ChangeMapChunkSize::setValue(Map *map, const QSize &value) const -{ - map->setChunkSize(value); - emit document()->changed(MapChangeEvent(Map::ChunkSizeProperty)); -} - - -ChangeMapStaggerAxis::ChangeMapStaggerAxis(MapDocument *document, Map::StaggerAxis staggerAxis) - : ChangeValue(document, { document->map() }, staggerAxis) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Stagger Axis")); -} - -Map::StaggerAxis ChangeMapStaggerAxis::getValue(const Map *map) const -{ - return map->staggerAxis(); -} - -void ChangeMapStaggerAxis::setValue(Map *map, const Map::StaggerAxis &value) const -{ - map->setStaggerAxis(value); - emit document()->changed(MapChangeEvent(Map::StaggerAxisProperty)); -} - - -ChangeMapStaggerIndex::ChangeMapStaggerIndex(MapDocument *document, Map::StaggerIndex staggerIndex) - : ChangeValue(document, { document->map() }, staggerIndex) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Stagger Index")); -} - -Map::StaggerIndex ChangeMapStaggerIndex::getValue(const Map *map) const -{ - return map->staggerIndex(); -} - -void ChangeMapStaggerIndex::setValue(Map *map, const Map::StaggerIndex &value) const -{ - map->setStaggerIndex(value); - emit document()->changed(MapChangeEvent(Map::StaggerIndexProperty)); -} - - -ChangeMapParallaxOrigin::ChangeMapParallaxOrigin(MapDocument *document, const QPointF ¶llaxOrigin) - : ChangeValue(document, { document->map() }, parallaxOrigin) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Parallax Origin")); -} - -QPointF ChangeMapParallaxOrigin::getValue(const Map *map) const -{ - return map->parallaxOrigin(); -} - -void ChangeMapParallaxOrigin::setValue(Map *map, const QPointF &value) const -{ - map->setParallaxOrigin(value); - emit document()->changed(MapChangeEvent(Map::ParallaxOriginProperty)); -} - - -ChangeMapOrientation::ChangeMapOrientation(MapDocument *document, Map::Orientation orientation) - : ChangeValue(document, { document->map() }, orientation) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Orientation")); -} - -Map::Orientation ChangeMapOrientation::getValue(const Map *map) const -{ - return map->orientation(); -} - -void ChangeMapOrientation::setValue(Map *map, const Map::Orientation &value) const -{ - map->setOrientation(value); - emit document()->changed(MapChangeEvent(Map::OrientationProperty)); -} - - -ChangeMapRenderOrder::ChangeMapRenderOrder(MapDocument *document, Map::RenderOrder renderOrder) - : ChangeValue(document, { document->map() }, renderOrder) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Render Order")); -} - -Map::RenderOrder ChangeMapRenderOrder::getValue(const Map *map) const -{ - return map->renderOrder(); -} - -void ChangeMapRenderOrder::setValue(Map *map, const Map::RenderOrder &value) const -{ - map->setRenderOrder(value); - emit document()->changed(MapChangeEvent(Map::RenderOrderProperty)); -} - - -ChangeMapLayerDataFormat::ChangeMapLayerDataFormat(MapDocument *document, Map::LayerDataFormat layerDataFormat) - : ChangeValue(document, { document->map() }, layerDataFormat) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Layer Data Format")); -} - -Map::LayerDataFormat ChangeMapLayerDataFormat::getValue(const Map *map) const -{ - return map->layerDataFormat(); -} - -void ChangeMapLayerDataFormat::setValue(Map *map, const Map::LayerDataFormat &value) const -{ - map->setLayerDataFormat(value); - emit document()->changed(MapChangeEvent(Map::LayerDataFormatProperty)); -} - - -ChangeMapTileSize::ChangeMapTileSize(MapDocument *document, const QSize &size) - : ChangeValue(document, { document->map() }, size) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Tile Size")); -} - -QSize ChangeMapTileSize::getValue(const Map *map) const -{ - return map->tileSize(); -} - -void ChangeMapTileSize::setValue(Map *map, const QSize &value) const -{ - map->setTileSize(value); - emit document()->changed(MapChangeEvent(Map::TileSizeProperty)); -} - - -ChangeMapInfinite::ChangeMapInfinite(MapDocument *document, bool infinite) - : ChangeValue(document, { document->map() }, infinite) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Infinite Property")); -} - -bool ChangeMapInfinite::getValue(const Map *map) const -{ - return map->infinite(); -} - -void ChangeMapInfinite::setValue(Map *map, const bool &value) const -{ - map->setInfinite(value); - emit document()->changed(MapChangeEvent(Map::InfiniteProperty)); -} - - -ChangeMapHexSideLength::ChangeMapHexSideLength(MapDocument *document, int hexSideLength) - : ChangeValue(document, { document->map() }, hexSideLength) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Hex Side Length")); -} - -int ChangeMapHexSideLength::getValue(const Map *map) const -{ - return map->hexSideLength(); -} - -void ChangeMapHexSideLength::setValue(Map *map, const int &value) const -{ - map->setHexSideLength(value); - emit document()->changed(MapChangeEvent(Map::HexSideLengthProperty)); -} - - -ChangeMapCompressionLevel::ChangeMapCompressionLevel(MapDocument *document, int compressionLevel) - : ChangeValue(document, { document->map() }, compressionLevel) -{ - setText(QCoreApplication::translate("Undo Commands", - "Change Compression Level")); -} - -int ChangeMapCompressionLevel::getValue(const Map *map) const -{ - return map->compressionLevel(); -} - -void ChangeMapCompressionLevel::setValue(Map *map, const int &value) const -{ - map->setCompressionLevel(value); - emit document()->changed(MapChangeEvent(Map::CompressionLevelProperty)); -} diff --git a/src/tiled/changemapproperty.h b/src/tiled/changemapproperty.h index 7c87becebf..3ea19d0237 100644 --- a/src/tiled/changemapproperty.h +++ b/src/tiled/changemapproperty.h @@ -1,6 +1,7 @@ /* * changemapproperty.h * Copyright 2012, Emmanuel Barroga emmanuelbarroga@gmail.com + * Copyright 2014-2024, Thorbjørn Lindeijer * * This file is part of Tiled. * @@ -26,164 +27,206 @@ #include "mapdocument.h" #include +#include namespace Tiled { class MapDocument; -class ChangeMapBackgroundColor : public ChangeValue +struct MapBackgroundColor { -public: - ChangeMapBackgroundColor(MapDocument *document, const QColor &backgroundColor); - - int id() const override { return Cmd_ChangeMapBackgroundColor; } - -private: - QColor getValue(const Map *map) const override; - void setValue(Map *map, const QColor &value) const override; + using Type = QColor; + + static void set(Map *map, Type value) { map->setBackgroundColor(value); } + static Type get(const Map *map) { return map->backgroundColor(); } + static int undoId() { return Cmd_ChangeMapBackgroundColor; } + static Map::Property property() { return Map::BackgroundColorProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Background Color"); + } }; - -class ChangeMapChunkSize : public ChangeValue +struct MapChunkSize { -public: - ChangeMapChunkSize(MapDocument *document, const QSize &chunkSize); - - int id() const override { return Cmd_ChangeMapChunkSize; } - -private: - QSize getValue(const Map *map) const override; - void setValue(Map *map, const QSize &value) const override; + using Type = QSize; + + static void set(Map *map, Type value) { map->setChunkSize(value); } + static Type get(const Map *map) { return map->chunkSize(); } + static int undoId() { return Cmd_ChangeMapChunkSize; } + static Map::Property property() { return Map::ChunkSizeProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Chunk Size"); + } }; - -class ChangeMapStaggerAxis : public ChangeValue +struct MapStaggerAxis { -public: - ChangeMapStaggerAxis(MapDocument *document, Map::StaggerAxis staggerAxis); - - int id() const override { return Cmd_ChangeMapStaggerAxis; } - -private: - Map::StaggerAxis getValue(const Map *map) const override; - void setValue(Map *map, const Map::StaggerAxis &value) const override; + using Type = Map::StaggerAxis; + + static void set(Map *map, Type value) { map->setStaggerAxis(value); } + static Type get(const Map *map) { return map->staggerAxis(); } + static int undoId() { return Cmd_ChangeMapStaggerAxis; } + static Map::Property property() { return Map::StaggerAxisProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Stagger Axis"); + } }; - -class ChangeMapStaggerIndex : public ChangeValue +struct MapStaggerIndex { -public: - ChangeMapStaggerIndex(MapDocument *document, Map::StaggerIndex staggerIndex); - - int id() const override { return Cmd_ChangeMapStaggerIndex; } - -private: - Map::StaggerIndex getValue(const Map *map) const override; - void setValue(Map *map, const Map::StaggerIndex &value) const override; + using Type = Map::StaggerIndex; + + static void set(Map *map, Type value) { map->setStaggerIndex(value); } + static Type get(const Map *map) { return map->staggerIndex(); } + static int undoId() { return Cmd_ChangeMapStaggerIndex; } + static Map::Property property() { return Map::StaggerIndexProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Stagger Index"); + } }; - -class ChangeMapParallaxOrigin : public ChangeValue +struct MapParallaxOrigin { -public: - ChangeMapParallaxOrigin(MapDocument *document, const QPointF ¶llaxOrigin); - - int id() const override { return Cmd_ChangeMapParallaxOrigin; } - -private: - QPointF getValue(const Map *map) const override; - void setValue(Map *map, const QPointF &value) const override; + using Type = QPointF; + + static void set(Map *map, Type value) { map->setParallaxOrigin(value); } + static Type get(const Map *map) { return map->parallaxOrigin(); } + static int undoId() { return Cmd_ChangeMapParallaxOrigin; } + static Map::Property property() { return Map::ParallaxOriginProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Parallax Origin"); + } }; - -class ChangeMapOrientation : public ChangeValue +struct MapOrientation { -public: - ChangeMapOrientation(MapDocument *document, Map::Orientation orientation); - - int id() const override { return Cmd_ChangeMapOrientation; } - -private: - Map::Orientation getValue(const Map *map) const override; - void setValue(Map *map, const Map::Orientation &value) const override; + using Type = Map::Orientation; + + static void set(Map *map, Type value) { map->setOrientation(value); } + static Type get(const Map *map) { return map->orientation(); } + static int undoId() { return Cmd_ChangeMapOrientation; } + static Map::Property property() { return Map::OrientationProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Orientation"); + } }; - -class ChangeMapRenderOrder : public ChangeValue +struct MapRenderOrder { -public: - ChangeMapRenderOrder(MapDocument *document, Map::RenderOrder renderOrder); - - int id() const override { return Cmd_ChangeMapRenderOrder; } - -private: - Map::RenderOrder getValue(const Map *map) const override; - void setValue(Map *map, const Map::RenderOrder &value) const override; + using Type = Map::RenderOrder; + + static void set(Map *map, Type value) { map->setRenderOrder(value); } + static Type get(const Map *map) { return map->renderOrder(); } + static int undoId() { return Cmd_ChangeMapRenderOrder; } + static Map::Property property() { return Map::RenderOrderProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Render Order"); + } }; - -class ChangeMapLayerDataFormat : public ChangeValue +struct MapLayerDataFormat { -public: - ChangeMapLayerDataFormat(MapDocument *document, Map::LayerDataFormat layerDataFormat); - - int id() const override { return Cmd_ChangeMapLayerDataFormat; } - -private: - Map::LayerDataFormat getValue(const Map *map) const override; - void setValue(Map *map, const Map::LayerDataFormat &value) const override; + using Type = Map::LayerDataFormat; + + static void set(Map *map, Type value) { map->setLayerDataFormat(value); } + static Type get(const Map *map) { return map->layerDataFormat(); } + static int undoId() { return Cmd_ChangeMapLayerDataFormat; } + static Map::Property property() { return Map::LayerDataFormatProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Layer Data Format"); + } }; - -class ChangeMapTileSize : public ChangeValue +struct MapTileSize { -public: - ChangeMapTileSize(MapDocument *document, const QSize &tileSize); - - int id() const override { return Cmd_ChangeMapTileSize; } - -private: - QSize getValue(const Map *map) const override; - void setValue(Map *map, const QSize &value) const override; + using Type = QSize; + + static void set(Map *map, Type value) { map->setTileSize(value); } + static Type get(const Map *map) { return map->tileSize(); } + static int undoId() { return Cmd_ChangeMapTileSize; } + static Map::Property property() { return Map::TileSizeProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Tile Size"); + } }; - -class ChangeMapInfinite : public ChangeValue +struct MapInfinite { -public: - ChangeMapInfinite(MapDocument *document, bool infinite); - - int id() const override { return Cmd_ChangeMapInfinite; } - -private: - bool getValue(const Map *map) const override; - void setValue(Map *map, const bool &value) const override; + using Type = bool; + + static void set(Map *map, Type value) { map->setInfinite(value); } + static Type get(const Map *map) { return map->infinite(); } + static int undoId() { return Cmd_ChangeMapInfinite; } + static Map::Property property() { return Map::InfiniteProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Infinite"); + } }; - -class ChangeMapHexSideLength : public ChangeValue +struct MapHexSideLength { -public: - ChangeMapHexSideLength(MapDocument *document, int hexSideLength); - - int id() const override { return Cmd_ChangeMapHexSideLength; } + using Type = int; + + static void set(Map *map, Type value) { map->setHexSideLength(value); } + static Type get(const Map *map) { return map->hexSideLength(); } + static int undoId() { return Cmd_ChangeMapHexSideLength; } + static Map::Property property() { return Map::HexSideLengthProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Hex Side Length"); + } +}; -private: - int getValue(const Map *map) const override; - void setValue(Map *map, const int &value) const override; +struct MapCompressionLevel +{ + using Type = int; + + static void set(Map *map, Type value) { map->setCompressionLevel(value); } + static Type get(const Map *map) { return map->compressionLevel(); } + static int undoId() { return Cmd_ChangeMapCompressionLevel; } + static Map::Property property() { return Map::CompressionLevelProperty; } + static QString undoName() { + return QCoreApplication::translate("Undo Commands", "Change Compression Level"); + } }; -class ChangeMapCompressionLevel : public ChangeValue +template +class ChangeMapProperty : public ChangeValue { public: - ChangeMapCompressionLevel(MapDocument *document, int compressionLevel); + ChangeMapProperty(MapDocument *document, const typename Member::Type &value) + : ChangeValue(document, { document->map() }, value) + { + QUndoCommand::setText(Member::undoName()); + } - int id() const override { return Cmd_ChangeMapCompressionLevel; } + int id() const override { return Member::undoId(); } private: - int getValue(const Map *map) const override; - void setValue(Map *map, const int &value) const override; + typename Member::Type getValue(const Map *map) const override + { + return Member::get(map); + } + + void setValue(Map *map, const typename Member::Type &value) const override + { + Member::set(map, value); + emit ChangeValue::document()->changed(MapChangeEvent(Member::property())); + } }; + +using ChangeMapBackgroundColor = ChangeMapProperty; +using ChangeMapChunkSize = ChangeMapProperty; +using ChangeMapStaggerAxis = ChangeMapProperty; +using ChangeMapStaggerIndex = ChangeMapProperty; +using ChangeMapParallaxOrigin = ChangeMapProperty; +using ChangeMapOrientation = ChangeMapProperty; +using ChangeMapRenderOrder = ChangeMapProperty; +using ChangeMapLayerDataFormat = ChangeMapProperty; +using ChangeMapTileSize = ChangeMapProperty; +using ChangeMapInfinite = ChangeMapProperty; +using ChangeMapHexSideLength = ChangeMapProperty; +using ChangeMapCompressionLevel = ChangeMapProperty; + } // namespace Tiled diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 4f6f7e8150..9106b2cd3d 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -135,7 +135,6 @@ DynamicLibrary { "changemapobject.h", "changemapobjectsorder.cpp", "changemapobjectsorder.h", - "changemapproperty.cpp", "changemapproperty.h", "changeobjectgroupproperties.cpp", "changeobjectgroupproperties.h", From 2cabfadcdf70c0d71495f779cbe630389ce66d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 31 Oct 2024 21:16:29 +0100 Subject: [PATCH 53/78] Remember the expanded state for built-in property groups It remembers the expanded state by their index, because this seems like the most desirable behavior. The built-in property groups are now children of a top-level GroupProperty with a new "ChildrenOnly" display mode. This mode suppresses the display of a collapsible header. --- src/tiled/propertieswidget.cpp | 310 ++++++++++++++++----------------- src/tiled/propertieswidget.h | 2 + src/tiled/varianteditor.cpp | 25 ++- src/tiled/varianteditor.h | 15 +- 4 files changed, 185 insertions(+), 167 deletions(-) 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; } From 13fe21e3cfcc35dcb7dbb46ab0542a5a16470b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 5 Nov 2024 15:51:22 +0100 Subject: [PATCH 54/78] Fixed initialization of SizeProperty::m_minimum This was causing the map size display to sometimes display way too large values. --- src/tiled/varianteditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 4693e36504..08b6a6554c 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -349,7 +349,7 @@ struct SizeProperty : PropertyTemplate void setSuffix(const QString &suffix) { m_suffix = suffix; } private: - int m_minimum; + int m_minimum = 0; QString m_suffix; }; From 921bce22f0d559da5405dd686dbdcf4b5500053f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 6 Nov 2024 13:20:15 +0100 Subject: [PATCH 55/78] Fixed flickering when rewrapping widget pairs Rather than rearranging widgets in a QGridLayout in the resize event, we now use a custom layout, which can propertly implement heightForWidth to ensure a smooth layout update. --- src/tiled/propertyeditorwidgets.cpp | 342 ++++++++++++++++++---------- src/tiled/propertyeditorwidgets.h | 41 +--- 2 files changed, 222 insertions(+), 161 deletions(-) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index c4255ccbbd..2a3673443e 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -31,6 +31,187 @@ namespace Tiled { +/** + * A layout for label/widget pairs, wrapping them either two per row or each on + * their own row, depending on the available space. + * + * The labels are forced to width of the widest label, while the widgets share + * the remaining space. + */ +class PairwiseWrappingLayout : public QLayout +{ + Q_OBJECT + +public: + struct WidgetPair { + QLabel *label; + QWidget *widget; + }; + + PairwiseWrappingLayout(const QVector &widgetPairs, + QWidget *parent); + ~PairwiseWrappingLayout(); + + void addItem(QLayoutItem *item) override + { m_items.append(item); } + + Qt::Orientations expandingDirections() const override + { return {}; } + + bool hasHeightForWidth() const override + { return true; } + + int heightForWidth(int width) const override + { return doLayout(QRect(0, 0, width, 0), true); } + + int count() const override + { return m_items.size(); } + + QLayoutItem *itemAt(int index) const override + { return m_items.value(index); } + + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + + QSize sizeHint() const override + { return minimumSize(); } + + QLayoutItem *takeAt(int index) override; + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int minimumTwoColumnWidth() const; + + QList m_items; +}; + +PairwiseWrappingLayout::PairwiseWrappingLayout(const QVector &widgetPairs, + QWidget *parent) + : QLayout(parent) +{ + setContentsMargins(QMargins()); + setSpacing(Utils::dpiScaled(2) * 2); + + const int horizontalMargin = Utils::dpiScaled(3); + + for (auto &pair : widgetPairs) { + pair.label->setAlignment(Qt::AlignCenter); + pair.label->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); + addWidget(pair.label); + addWidget(pair.widget); + } +} + +PairwiseWrappingLayout::~PairwiseWrappingLayout() +{ + while (QLayoutItem *item = takeAt(0)) + delete item; +} + +QLayoutItem *PairwiseWrappingLayout::takeAt(int index) +{ + if (index >= 0 && index < m_items.size()) + return m_items.takeAt(index); + return nullptr; +} + +void PairwiseWrappingLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize PairwiseWrappingLayout::minimumSize() const +{ + QSize size; + size.setWidth(minimumTwoColumnWidth()); + size.setHeight(doLayout(QRect(0, 0, size.width(), 0), true)); + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const auto margins = contentsMargins(); + return QSize(size.width() + margins.left() + margins.right(), + size.height() + margins.top() + margins.bottom()); +#else + return size.grownBy(contentsMargins()); +#endif +} + +int PairwiseWrappingLayout::doLayout(const QRect &rect, bool testOnly) const +{ + const auto margins = contentsMargins(); + const QRect effectiveRect = rect.marginsRemoved(margins); + const int spacing = QLayout::spacing(); + const int columns = effectiveRect.width() < minimumTwoColumnWidth() ? 1 : 2; + + // For simplicity, all lines will have the same height. Even columns will + // get their width from the widest label, whereas odd columns will share + // the remaining space. + int lineHeight = 0; + int maxLabelWidth = 0; + for (qsizetype i = 0; i < m_items.size(); ++i) { + const auto sizeHint = m_items.at(i)->minimumSize(); + lineHeight = qMax(lineHeight, sizeHint.height()); + if (i % 2 == 0) + maxLabelWidth = qMax(maxLabelWidth, sizeHint.width()); + } + + if (testOnly) { + const int lines = (m_items.size() / 2 + columns - 1) / columns; + return margins.top() + margins.bottom() + + lines * (lineHeight + spacing) - spacing; + } + + int totalWidgetWidth = (effectiveRect.width() - + maxLabelWidth * columns - + spacing * (columns * 2 - 1)); + QList widgetWidths; + for (int i = columns; i > 0; --i) { + widgetWidths.append(totalWidgetWidth / i); + totalWidgetWidth -= widgetWidths.last(); + } + + int x = effectiveRect.x(); + int y = effectiveRect.y(); + for (qsizetype i = 0; i < m_items.size(); ++i) { + const int column = i / 2 % columns; + const QSize size((i % 2 == 0) ? maxLabelWidth + : widgetWidths.at(column), + lineHeight); + + m_items.at(i)->setGeometry(QRect(QPoint(x, y), size)); + x += size.width() + spacing; + + if (column == columns - 1 && (i % 2 == 1)) { + x = effectiveRect.x(); + y += lineHeight + spacing; + } + } + + return 0; +} + +int PairwiseWrappingLayout::minimumTwoColumnWidth() const +{ + const int spacing = QLayout::spacing(); + int sum = 0; + int minimum = 0; + int index = 0; + + for (qsizetype i = 0; i < m_items.size() - 1; i += 2) { + sum += (m_items.at(i)->minimumSize().width() + + m_items.at(i + 1)->minimumSize().width() + + spacing * 2); + + if (++index % 2 == 0) { + minimum = std::max(sum - spacing, minimum); + sum = 0; + } + } + + return minimum; +} + + /** * Returns whether the given event is a shortcut override event for the undo or * redo shortcuts. We generally want to use the global undo and redo shortcuts @@ -198,109 +379,17 @@ void DoubleSpinBox::wheelEvent(QWheelEvent *event) } -ResponsivePairswiseWidget::ResponsivePairswiseWidget(QWidget *parent) - : QWidget(parent) -{ - auto layout = new QGridLayout(this); - layout->setContentsMargins(QMargins()); - layout->setColumnStretch(1, 1); - layout->setSpacing(Utils::dpiScaled(4)); -} - -void ResponsivePairswiseWidget::setWidgetPairs(const QVector &widgetPairs) -{ - const int horizontalMargin = Utils::dpiScaled(3); - - for (auto &pair : widgetPairs) { - pair.label->setAlignment(Qt::AlignCenter); - pair.label->setContentsMargins(horizontalMargin, 0, horizontalMargin, 0); - } - - m_widgetPairs = widgetPairs; - - addWidgetsToLayout(); -} - -void ResponsivePairswiseWidget::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - - const auto orientation = event->size().width() < minimumHorizontalWidth() - ? Qt::Vertical : Qt::Horizontal; - - if (m_orientation != orientation) { - m_orientation = orientation; - - auto layout = this->layout(); - - // Remove all widgets from layout, without deleting them - for (auto &pair : m_widgetPairs) { - layout->removeWidget(pair.label); - layout->removeWidget(pair.widget); - } - - addWidgetsToLayout(); - - // This avoids flickering when the layout changes - layout->activate(); - } -} - -void ResponsivePairswiseWidget::addWidgetsToLayout() -{ - auto layout = qobject_cast(this->layout()); - - const int maxColumns = m_orientation == Qt::Horizontal ? 4 : 2; - int row = 0; - int column = 0; - - for (auto &pair : m_widgetPairs) { - layout->addWidget(pair.label, row, column); - layout->addWidget(pair.widget, row, column + 1); - column += 2; - - if (column == maxColumns) { - column = 0; - ++row; - } - } - - layout->setColumnStretch(3, m_orientation == Qt::Horizontal ? 1 : 0); -} - -int ResponsivePairswiseWidget::minimumHorizontalWidth() const -{ - const int spacing = layout()->spacing(); - int sum = 0; - int minimum = 0; - int index = 0; - - for (auto &pair : m_widgetPairs) { - sum += (pair.label->minimumSizeHint().width() + - pair.widget->minimumSizeHint().width() + - spacing * 2); - - if (++index % 2 == 0) { - minimum = std::max(sum - spacing, minimum); - sum = 0; - } - } - - return minimum; -} - - SizeEdit::SizeEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_widthLabel(new QLabel(QStringLiteral("W"), this)) , m_heightLabel(new QLabel(QStringLiteral("H"), this)) , m_widthSpinBox(new SpinBox(this)) , m_heightSpinBox(new SpinBox(this)) { - setWidgetPairs({ - { m_widthLabel, m_widthSpinBox }, - { m_heightLabel, m_heightSpinBox }, - }); + new PairwiseWrappingLayout({ + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }, this); connect(m_widthSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); connect(m_heightSpinBox, qOverload(&QSpinBox::valueChanged), this, &SizeEdit::valueChanged); @@ -332,16 +421,16 @@ void SizeEdit::setSuffix(const QString &suffix) SizeFEdit::SizeFEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_widthLabel(new QLabel(QStringLiteral("W"), this)) , m_heightLabel(new QLabel(QStringLiteral("H"), this)) , m_widthSpinBox(new DoubleSpinBox(this)) , m_heightSpinBox(new DoubleSpinBox(this)) { - setWidgetPairs({ - { m_widthLabel, m_widthSpinBox }, - { m_heightLabel, m_heightSpinBox }, - }); + new PairwiseWrappingLayout({ + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }, this); connect(m_widthSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &SizeFEdit::valueChanged); connect(m_heightSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &SizeFEdit::valueChanged); @@ -361,16 +450,16 @@ QSizeF SizeFEdit::value() const PointEdit::PointEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_xLabel(new QLabel(QStringLiteral("X"), this)) , m_yLabel(new QLabel(QStringLiteral("Y"), this)) , m_xSpinBox(new SpinBox(this)) , m_ySpinBox(new SpinBox(this)) { - setWidgetPairs({ - { m_xLabel, m_xSpinBox }, - { m_yLabel, m_ySpinBox }, - }); + new PairwiseWrappingLayout({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + }, this); connect(m_xSpinBox, qOverload(&QSpinBox::valueChanged), this, &PointEdit::valueChanged); connect(m_ySpinBox, qOverload(&QSpinBox::valueChanged), this, &PointEdit::valueChanged); @@ -396,16 +485,16 @@ void PointEdit::setSuffix(const QString &suffix) PointFEdit::PointFEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_xLabel(new QLabel(QStringLiteral("X"), this)) , m_yLabel(new QLabel(QStringLiteral("Y"), this)) , m_xSpinBox(new DoubleSpinBox(this)) , m_ySpinBox(new DoubleSpinBox(this)) { - setWidgetPairs({ - { m_xLabel, m_xSpinBox }, - { m_yLabel, m_ySpinBox }, - }); + new PairwiseWrappingLayout({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + }, this); connect(m_xSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &PointFEdit::valueChanged); connect(m_ySpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &PointFEdit::valueChanged); @@ -431,7 +520,7 @@ void PointFEdit::setSingleStep(double step) RectEdit::RectEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_xLabel(new QLabel(QStringLiteral("X"), this)) , m_yLabel(new QLabel(QStringLiteral("Y"), this)) , m_widthLabel(new QLabel(QStringLiteral("W"), this)) @@ -441,12 +530,12 @@ RectEdit::RectEdit(QWidget *parent) , m_widthSpinBox(new SpinBox(this)) , m_heightSpinBox(new SpinBox(this)) { - setWidgetPairs({ - { m_xLabel, m_xSpinBox }, - { m_yLabel, m_ySpinBox }, - { m_widthLabel, m_widthSpinBox }, - { m_heightLabel, m_heightSpinBox }, - }); + new PairwiseWrappingLayout({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }, this); m_widthSpinBox->setMinimum(0); m_heightSpinBox->setMinimum(0); @@ -492,7 +581,7 @@ void RectEdit::setConstraint(const QRect &constraint) RectFEdit::RectFEdit(QWidget *parent) - : ResponsivePairswiseWidget(parent) + : QWidget(parent) , m_xLabel(new QLabel(QStringLiteral("X"), this)) , m_yLabel(new QLabel(QStringLiteral("Y"), this)) , m_widthLabel(new QLabel(QStringLiteral("W"), this)) @@ -502,12 +591,12 @@ RectFEdit::RectFEdit(QWidget *parent) , m_widthSpinBox(new DoubleSpinBox(this)) , m_heightSpinBox(new DoubleSpinBox(this)) { - setWidgetPairs({ - { m_xLabel, m_xSpinBox }, - { m_yLabel, m_ySpinBox }, - { m_widthLabel, m_widthSpinBox }, - { m_heightLabel, m_heightSpinBox }, - }); + new PairwiseWrappingLayout({ + { m_xLabel, m_xSpinBox }, + { m_yLabel, m_ySpinBox }, + { m_widthLabel, m_widthSpinBox }, + { m_heightLabel, m_heightSpinBox }, + }, this); connect(m_xSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); connect(m_ySpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &RectFEdit::valueChanged); @@ -738,3 +827,4 @@ QSize PropertyLabel::sizeHint() const } // namespace Tiled #include "moc_propertyeditorwidgets.cpp" +#include "propertyeditorwidgets.moc" diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 070faadf43..0887ea3226 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -102,39 +102,10 @@ class DoubleSpinBox : public QDoubleSpinBox void wheelEvent(QWheelEvent *event) override; }; -/** - * A widget that shows label/widget pairs, wrapping them either two per row - * or each on their own row, depending on the available space. - */ -class ResponsivePairswiseWidget : public QWidget -{ - Q_OBJECT - -public: - struct WidgetPair { - QLabel *label; - QWidget *widget; - }; - - ResponsivePairswiseWidget(QWidget *parent = nullptr); - - void setWidgetPairs(const QVector &widgetPairs); - -protected: - void resizeEvent(QResizeEvent *event) override; - -private: - void addWidgetsToLayout(); - int minimumHorizontalWidth() const; - - Qt::Orientation m_orientation = Qt::Horizontal; - QVector m_widgetPairs; -}; - /** * A widget for editing a QSize value. */ -class SizeEdit : public ResponsivePairswiseWidget +class SizeEdit : public QWidget { Q_OBJECT Q_PROPERTY(QSize value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -161,7 +132,7 @@ class SizeEdit : public ResponsivePairswiseWidget /** * A widget for editing a QSizeF value. */ -class SizeFEdit : public ResponsivePairswiseWidget +class SizeFEdit : public QWidget { Q_OBJECT Q_PROPERTY(QSizeF value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -185,7 +156,7 @@ class SizeFEdit : public ResponsivePairswiseWidget /** * A widget for editing a QPoint value. */ -class PointEdit : public ResponsivePairswiseWidget +class PointEdit : public QWidget { Q_OBJECT Q_PROPERTY(QPoint value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -211,7 +182,7 @@ class PointEdit : public ResponsivePairswiseWidget /** * A widget for editing a QPointF value. */ -class PointFEdit : public ResponsivePairswiseWidget +class PointFEdit : public QWidget { Q_OBJECT Q_PROPERTY(QPointF value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -237,7 +208,7 @@ class PointFEdit : public ResponsivePairswiseWidget /** * A widget for editing a QRect value. */ -class RectEdit : public ResponsivePairswiseWidget +class RectEdit : public QWidget { Q_OBJECT Q_PROPERTY(QRect value READ value WRITE setValue NOTIFY valueChanged FINAL) @@ -267,7 +238,7 @@ class RectEdit : public ResponsivePairswiseWidget /** * A widget for editing a QRectF value. */ -class RectFEdit : public ResponsivePairswiseWidget +class RectFEdit : public QWidget { Q_OBJECT Q_PROPERTY(QRectF value READ value WRITE setValue NOTIFY valueChanged FINAL) From 9711234b072041227589afb0dda3aca568a766ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 6 Nov 2024 13:30:47 +0100 Subject: [PATCH 56/78] Fixed opacity slider to also ignore wheel events when not focused Since handling wheel events interferes with scrolling through the properties. --- src/tiled/propertyeditorwidgets.cpp | 9 +++++++++ src/tiled/propertyeditorwidgets.h | 15 ++++++++++++++- src/tiled/varianteditor.cpp | 6 +++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 2a3673443e..51bceb7a52 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -250,6 +250,15 @@ static QString removeRedundantTrialingZeros(const QString &text) } +void Slider::wheelEvent(QWheelEvent *event) +{ + if (!hasFocus()) + event->ignore(); + else + QSlider::wheelEvent(event); +} + + bool LineEdit::event(QEvent *event) { if (isUndoRedoShortcutOverride(event)) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 0887ea3226..3727d3bfa2 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -29,6 +29,20 @@ class QLabel; namespace Tiled { +/** + * A slider that doesn't respond to wheel events when not focused. + */ +class Slider : public QSlider +{ + Q_OBJECT + +public: + using QSlider::QSlider; + +protected: + void wheelEvent(QWheelEvent *event) override; +}; + /** * A line edit that doesn't override global undo/redo shortcuts. */ @@ -43,7 +57,6 @@ class LineEdit : public QLineEdit bool event(QEvent *event) override; }; - /** * A combo box that doesn't respond to wheel events when not focused and * doesn't override global undo/redo shortcuts. diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 5c55782d79..f9f69ad1bf 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -94,7 +94,7 @@ void GroupProperty::setExpanded(bool expanded) void GroupProperty::expandAll() { setExpanded(true); - for (auto property : m_subProperties) + for (auto property : std::as_const(m_subProperties)) if (auto groupProperty = qobject_cast(property)) groupProperty->expandAll(); } @@ -102,7 +102,7 @@ void GroupProperty::expandAll() void GroupProperty::collapseAll() { setExpanded(false); - for (auto property : m_subProperties) + for (auto property : std::as_const(m_subProperties)) if (auto groupProperty = qobject_cast(property)) groupProperty->collapseAll(); } @@ -177,7 +177,7 @@ QWidget *IntProperty::createEditor(QWidget *parent) layout->setContentsMargins(QMargins()); if (m_sliderEnabled) { - QSlider *slider = new QSlider(Qt::Horizontal, widget); + auto slider = new Slider(Qt::Horizontal, widget); slider->setRange(m_minimum, m_maximum); slider->setSingleStep(m_singleStep); slider->setValue(value()); From 023ba4cd83fa8273f4f86c7f917ad44b55b77e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 6 Nov 2024 13:55:21 +0100 Subject: [PATCH 57/78] Group object "Position" and "Size" properties as "Geometry" This is mostly to improve the layout since the W is often wider than the X and Y. The minimum size hint for the DoubleSpinBox was increased a bit so it wraps a bit earlier. This change also makes it so that changing any of these values changes only that particular value for all selected objects. --- src/tiled/propertieswidget.cpp | 83 +++++++++++++++++++++++------ src/tiled/propertyeditorwidgets.cpp | 2 +- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 5e6d8fb3f2..f8a66db874 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -44,6 +44,7 @@ #include "tilesetchanges.h" #include "tilesetdocument.h" #include "tilesetparametersedit.h" +#include "transformmapobjects.h" #include "utils.h" #include "varianteditor.h" #include "variantpropertymanager.h" @@ -1624,16 +1625,68 @@ class MapObjectProperties : public ObjectProperties return mapObject()->position(); }, [this](const QPointF &value) { - changeMapObject(MapObject::PositionProperty, value); + const auto oldValue = mapObject()->position(); + const bool changedX = oldValue.x() != value.x(); + const bool changedY = oldValue.y() != value.y(); + + auto objects = mapDocument()->selectedObjects(); + QVector states; + states.reserve(objects.size()); + + for (MapObject *object : objects) { + states.append(TransformState(object)); + auto &state = states.last(); + + auto position = state.position(); + + if (changedX) + position.setX(value.x()); + if (changedY) + position.setY(value.y()); + + state.setPosition(position); + } + + push(new TransformMapObjects(mDocument, objects, states)); }); - mSizeProperty = new SizeFProperty( - tr("Size"), + mBoundsProperty = new RectFProperty( + tr("Geometry"), [this] { - return mapObject()->size(); + return mapObject()->bounds(); }, - [this](const QSizeF &value) { - changeMapObject(MapObject::SizeProperty, value); + [this](const QRectF &value) { + const auto oldValue = mapObject()->bounds(); + const bool changedX = oldValue.x() != value.x(); + const bool changedY = oldValue.y() != value.y(); + const bool changedWidth = oldValue.width() != value.width(); + const bool changedHeight = oldValue.height() != value.height(); + + auto objects = mapDocument()->selectedObjects(); + QVector states; + states.reserve(objects.size()); + + for (MapObject *object : objects) { + states.append(TransformState(object)); + auto &state = states.last(); + + auto position = state.position(); + auto size = state.size(); + + if (changedX) + position.setX(value.x()); + if (changedY) + position.setY(value.y()); + if (changedWidth && object->hasDimensions()) + size.setWidth(value.width()); + if (changedHeight && object->hasDimensions()) + size.setHeight(value.height()); + + state.setPosition(position); + state.setSize(size); + } + + push(new TransformMapObjects(mDocument, objects, states)); }); mRotationProperty = new FloatProperty( @@ -1735,10 +1788,10 @@ class MapObjectProperties : public ObjectProperties if (mapDocument()->allowHidingObjects()) mObjectProperties->addProperty(mVisibleProperty); - mObjectProperties->addProperty(mPositionProperty); - if (mapObject()->hasDimensions()) - mObjectProperties->addProperty(mSizeProperty); + mObjectProperties->addProperty(mBoundsProperty); + else + mObjectProperties->addProperty(mPositionProperty); if (mapObject()->canRotate()) mObjectProperties->addProperty(mRotationProperty); @@ -1779,10 +1832,12 @@ class MapObjectProperties : public ObjectProperties emit mNameProperty->valueChanged(); if (change.properties & MapObject::VisibleProperty) emit mVisibleProperty->valueChanged(); - if (change.properties & MapObject::PositionProperty) + if (change.properties & MapObject::PositionProperty) { emit mPositionProperty->valueChanged(); + emit mBoundsProperty->valueChanged(); + } if (change.properties & MapObject::SizeProperty) - emit mSizeProperty->valueChanged(); + emit mBoundsProperty->valueChanged(); if (change.properties & MapObject::RotationProperty) emit mRotationProperty->valueChanged(); if (change.properties & MapObject::CellProperty) @@ -1802,7 +1857,7 @@ class MapObjectProperties : public ObjectProperties void updateEnabledState() { mVisibleProperty->setEnabled(mapDocument()->allowHidingObjects()); - mSizeProperty->setEnabled(mapObject()->hasDimensions()); + mBoundsProperty->setEnabled(mapObject()->hasDimensions()); mRotationProperty->setEnabled(mapObject()->canRotate()); mFlippingProperty->setEnabled(mapObject()->isTileObject()); @@ -1826,8 +1881,6 @@ class MapObjectProperties : public ObjectProperties void changeMapObject(MapObject::Property property, const QVariant &value) { - // todo: when changing object position or size, only change the - // selected objects in the respective axis or dimension QUndoCommand *command = new ChangeMapObject(mapDocument(), mapObject(), property, value); @@ -1857,7 +1910,7 @@ class MapObjectProperties : public ObjectProperties Property *mNameProperty; BoolProperty *mVisibleProperty; Property *mPositionProperty; - Property *mSizeProperty; + Property *mBoundsProperty; FloatProperty *mRotationProperty; // for tile objects diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 51bceb7a52..741569715f 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -356,7 +356,7 @@ QSize DoubleSpinBox::minimumSizeHint() const { // Don't adjust the horizontal size hint based on the maximum value. auto hint = QDoubleSpinBox::minimumSizeHint(); - hint.setWidth(Utils::dpiScaled(50)); + hint.setWidth(Utils::dpiScaled(65)); return hint; } From a7777eb9724ba8ebfcb67e4c027ff5f6e51fe578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 8 Nov 2024 14:55:41 +0100 Subject: [PATCH 58/78] Used the new properties widget in the Custom Types Editor * Moved VariantMapProperty to its own file, because it is now also used by the PropertyTypesEditor. * Introduced VariantEditorView to share a bit of code. --- src/tiled/libtilededitor.qbs | 2 + src/tiled/objectrefedit.h | 1 - src/tiled/propertieswidget.cpp | 514 +--------------------------- src/tiled/propertieswidget.h | 5 +- src/tiled/propertyeditorwidgets.cpp | 4 +- src/tiled/propertytypeseditor.cpp | 101 +++--- src/tiled/propertytypeseditor.h | 14 +- src/tiled/varianteditor.cpp | 25 +- src/tiled/varianteditor.h | 17 + src/tiled/variantmapproperty.cpp | 467 +++++++++++++++++++++++++ src/tiled/variantmapproperty.h | 89 +++++ 11 files changed, 657 insertions(+), 582 deletions(-) create mode 100644 src/tiled/variantmapproperty.cpp create mode 100644 src/tiled/variantmapproperty.h diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 9106b2cd3d..6e096753af 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -558,6 +558,8 @@ DynamicLibrary { "varianteditor.h", "varianteditorfactory.cpp", "varianteditorfactory.h", + "variantmapproperty.cpp", + "variantmapproperty.h", "variantpropertymanager.cpp", "variantpropertymanager.h", "wangbrush.cpp", diff --git a/src/tiled/objectrefedit.h b/src/tiled/objectrefedit.h index bb518f6577..ebcf94bdfb 100644 --- a/src/tiled/objectrefedit.h +++ b/src/tiled/objectrefedit.h @@ -20,7 +20,6 @@ #pragma once -#include "properties.h" #include "variantpropertymanager.h" #include diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index f8a66db874..cc02247b15 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -36,7 +36,6 @@ #include "compression.h" #include "mapdocument.h" #include "objectgroup.h" -#include "objectrefedit.h" #include "objecttemplate.h" #include "preferences.h" #include "propertybrowser.h" @@ -47,7 +46,7 @@ #include "transformmapobjects.h" #include "utils.h" #include "varianteditor.h" -#include "variantpropertymanager.h" +#include "variantmapproperty.h" #include "wangoverlay.h" #include @@ -258,31 +257,6 @@ class FlippingProperty : public IntProperty }; -class ObjectRefProperty : public PropertyTemplate -{ - Q_OBJECT - -public: - using PropertyTemplate::PropertyTemplate; - - QWidget *createEditor(QWidget *parent) override - { - auto editor = new ObjectRefEdit(parent); - auto syncEditor = [this, editor] { - const QSignalBlocker blocker(editor); - editor->setValue(value()); - }; - syncEditor(); - connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, &ObjectRefEdit::valueChanged, - this, [this](const DisplayObjectRef &value) { - setValue(value); - }); - return editor; - } -}; - - class ImageLayerRepeatProperty : public PropertyTemplate { Q_OBJECT @@ -469,66 +443,6 @@ static bool objectPropertiesRelevant(Document *document, Object *object) return false; } - -class VariantMapProperty : public GroupProperty -{ - Q_OBJECT - -public: - VariantMapProperty(const QString &name, QObject *parent = nullptr); - - void setValue(const QVariantMap &value, - const QVariantMap &suggestions = {}); - - const QVariantMap &value() const { return mValue; } - -signals: - void memberValueChanged(const QStringList &path, const QVariant &value); - void renameRequested(const QString &name); - -protected: - Document *mDocument = nullptr; - -private: - bool createOrUpdateProperty(int index, - const QString &name, - const QVariant &oldValue, - const QVariant &newValue); - Property *createProperty(const QStringList &path, - std::function get, - std::function set); - - void createClassMembers(const QStringList &path, - GroupProperty *groupProperty, - const ClassPropertyType &classType, - std::function get); - - void removeMember(const QString &name); - void addMember(const QString &name, const QVariant &value); - void setClassMember(const QStringList &path, const QVariant &value); - - void updateModifiedRecursively(Property *property, const QVariant &value); - void emitValueChangedRecursively(Property *property); - - void propertyTypesChanged(); - - void emitMemberValueChanged(const QStringList &path, const QVariant &value); - - void memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos); - - QIcon m_resetIcon; - QIcon m_removeIcon; - QIcon m_addIcon; - QIcon m_renameIcon; - bool mEmittingValueChanged = false; - bool mPropertyTypesChanged = false; - QVariantMap mValue; - QVariantMap mSuggestions; - QHash mPropertyMap; - QSet mExpandedProperties; -}; - - class CustomProperties : public VariantMapProperty { Q_OBJECT @@ -543,6 +457,13 @@ class CustomProperties : public VariantMapProperty void setDocument(Document *document); +protected: + void propertyTypesChanged() override + { + QScopedValueRollback propertyTypesChanged(mPropertyTypesChanged, true); + refresh(); + } + private: void onChanged(const ChangeEvent &change) { if (change.type != ChangeEvent::ObjectsChanged) @@ -2240,21 +2161,8 @@ class WangColorProperties : public ObjectProperties PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} , mCustomProperties(new CustomProperties) - , mScrollArea(new QScrollArea(this)) + , mPropertyBrowser(new VariantEditorView(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"))); @@ -2289,7 +2197,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - layout->addWidget(mScrollArea); + layout->addWidget(mPropertyBrowser); layout->addWidget(toolBar); setLayout(layout); @@ -2449,408 +2357,6 @@ void PropertiesWidget::currentObjectChanged(Object *object) } -VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) - : GroupProperty(name, parent) - , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) - , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) - , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) - , m_renameIcon(QIcon(QLatin1String(":/images/16/rename.png"))) -{ - m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); - m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); - m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); - - connect(Preferences::instance(), &Preferences::propertyTypesChanged, - this, &VariantMapProperty::propertyTypesChanged); -} - -void VariantMapProperty::setValue(const QVariantMap &value, - const QVariantMap &suggestions) -{ - QVariantMap oldProperties = mSuggestions; - mergeProperties(oldProperties, mValue); - - mValue = value; - mSuggestions = suggestions; - - QVariantMap newProperties = suggestions; - mergeProperties(newProperties, value); - - // First, delete all properties that are not in allProperties - for (auto it = mPropertyMap.begin(); it != mPropertyMap.end(); ) { - if (newProperties.contains(it.key())) { - ++it; - } else { - deleteProperty(it.value()); - it = mPropertyMap.erase(it); - } - } - - int index = 0; - - QMapIterator it(newProperties); - while (it.hasNext()) { - it.next(); - const QString &name = it.key(); - const auto &newValue = it.value(); - const auto oldValue = oldProperties.value(name); - - if (createOrUpdateProperty(index, name, oldValue, newValue)) - ++index; - } - - QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); - emit valueChanged(); -} - -static bool canReuseProperty(const QVariant &a, - const QVariant &b, - bool propertyTypesChanged) -{ - if (a.userType() != b.userType()) - return false; - - // Two PropertyValue values might still have different types - if (a.userType() == propertyValueId()) { - // Trigger re-creation of the property when the types have changed - if (propertyTypesChanged) - return false; - - auto aTypeId = a.value().typeId; - auto bTypeId = b.value().typeId; - if (aTypeId != bTypeId) - return false; - } - - return true; -} - -bool VariantMapProperty::createOrUpdateProperty(int index, - const QString &name, - const QVariant &oldValue, - const QVariant &newValue) -{ - auto property = mPropertyMap.value(name); - - // If it already exists, check whether we need to delete it - if (property && !canReuseProperty(oldValue, newValue, mPropertyTypesChanged)) { - deleteProperty(property); - mPropertyMap.remove(name); - property = nullptr; - } - - // Try to create the property if necessary - if (!property) { - auto get = [=] { - return mValue.value(name, mSuggestions.value(name)); - }; - auto set = [=] (const QVariant &value) { - mValue.insert(name, value); - emitMemberValueChanged({ name }, value); - }; - - property = createProperty({ name }, std::move(get), std::move(set)); - if (property) { - connect(property, &Property::removeRequested, this, [=] { - removeMember(name); - }); - - connect(property, &Property::addRequested, this, [=] { - addMember(name, mSuggestions.value(name)); - }); - - insertProperty(index, property); - mPropertyMap.insert(name, property); - } else { - qWarning() << "Failed to create property for" << name - << "with type" << newValue.typeName(); - } - } - - if (property) { - if (mValue.contains(name)) { - property->setEnabled(true); - property->setActions(Property::Action::Remove); - } else { - property->setEnabled(false); - property->setActions(Property::Action::Add); - } - - updateModifiedRecursively(property, newValue); - emitValueChangedRecursively(property); - } - - return true; -} - -Property *VariantMapProperty::createProperty(const QStringList &path, - std::function get, - std::function set) -{ - Property *property = nullptr; - - const auto value = get(); - const auto type = value.userType(); - const auto &name = path.last(); - QString typeName; - - if (type == filePathTypeId()) { - auto getUrl = [get = std::move(get)] { return get().value().url; }; - auto setUrl = [set = std::move(set)] (const QUrl &value) { - set(QVariant::fromValue(FilePath { value })); - }; - property = new UrlProperty(name, std::move(getUrl), std::move(setUrl)); - } else if (type == objectRefTypeId()) { - auto getObjectRef = [get = std::move(get), this] { - return DisplayObjectRef(get().value(), - static_cast(mDocument)); - }; - auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { - set(QVariant::fromValue(value.ref)); - }; - property = new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); - } else if (type == propertyValueId()) { - const auto propertyValue = value.value(); - if (auto propertyType = propertyValue.type()) { - switch (propertyType->type) { - case PropertyType::PT_Invalid: - break; - case PropertyType::PT_Class: { - auto classType = static_cast(*propertyType); - - auto groupProperty = new GroupProperty(name); - groupProperty->setHeader(false); - - createClassMembers(path, groupProperty, classType, std::move(get)); - - groupProperty->setExpanded(mExpandedProperties.contains(path)); - - connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { - if (expanded) - mExpandedProperties.insert(path); - else - mExpandedProperties.remove(path); - }); - - property = groupProperty; - break; - } - case PropertyType::PT_Enum: { - auto enumProperty = new BaseEnumProperty( - name, - [get = std::move(get)] { return get().value().value.toInt(); }, - [set = std::move(set), propertyType](int value) { - set(propertyType->wrap(value)); - }); - - auto enumType = static_cast(*propertyType); - enumProperty->setEnumData(enumType.values); - enumProperty->setFlags(enumType.valuesAsFlags); - - property = enumProperty; - break; - } - } - - typeName = propertyType->name; - } else { - typeName = tr("Unknown type"); - } - } else { - property = createVariantProperty(name, std::move(get), std::move(set)); - } - - if (property) { - if (typeName.isEmpty()) - typeName = typeToName(type); - - property->setToolTip(QStringLiteral("%1 : %2") - .arg(property->name(), typeName)); - - connect(property, &Property::contextMenuRequested, this, [=](const QPoint &globalPos) { - memberContextMenuRequested(property, path, globalPos); - }); - } - - return property; -} - -void VariantMapProperty::createClassMembers(const QStringList &path, - GroupProperty *groupProperty, - const ClassPropertyType &classType, - std::function get) -{ - // Create a sub-property for each member - QMapIterator it(classType.members); - while (it.hasNext()) { - it.next(); - const QString &name = it.key(); - - auto childPath = path; - childPath.append(name); - - auto getMember = [=] { - auto def = classType.members.value(name); - return get().value().value.toMap().value(name, def); - }; - auto setMember = [=] (const QVariant &value) { - setClassMember(childPath, value); - }; - - if (auto childProperty = createProperty(childPath, std::move(getMember), setMember)) { - childProperty->setActions(Property::Action::Reset); - groupProperty->addProperty(childProperty); - - connect(childProperty, &Property::resetRequested, this, [=] { - setMember(QVariant()); - emitValueChangedRecursively(childProperty); - }); - } - } -} - -void VariantMapProperty::removeMember(const QString &name) -{ - if (!mValue.contains(name)) - return; - - const auto oldValue = mValue.take(name); - - if (!mSuggestions.contains(name)) { - if (auto property = mPropertyMap.take(name)) - deleteProperty(property); - } else if (auto property = mPropertyMap.value(name)) { - const auto newValue = mSuggestions.value(name); - const int index = indexOfProperty(property); - createOrUpdateProperty(index, name, oldValue, newValue); - } - - emitMemberValueChanged({ name }, QVariant()); -} - -void VariantMapProperty::addMember(const QString &name, const QVariant &value) -{ - const auto oldValue = mValue.value(name, mSuggestions.value(name)); - - mValue.insert(name, value); - - if (auto property = mPropertyMap.value(name)) { - const int index = indexOfProperty(property); - createOrUpdateProperty(index, name, oldValue, value); - } - - emitMemberValueChanged({ name }, value); -} - -void VariantMapProperty::setClassMember(const QStringList &path, const QVariant &value) -{ - if (!setPropertyMemberValue(mValue, path, value)) - return; - - const auto &topLevelName = path.first(); - updateModifiedRecursively(mPropertyMap.value(topLevelName), - mValue.value(topLevelName)); - - emitMemberValueChanged(path, value); -} - -void VariantMapProperty::updateModifiedRecursively(Property *property, - const QVariant &value) -{ - auto groupProperty = qobject_cast(property); - if (!groupProperty) - return; - - const QVariantMap classValue = value.value().value.toMap(); - - for (auto subProperty : groupProperty->subProperties()) { - const auto &name = subProperty->name(); - const bool isModified = classValue.contains(name); - - if (subProperty->isModified() != isModified || subProperty->isModified()) { - subProperty->setModified(isModified); - updateModifiedRecursively(subProperty, classValue.value(name)); - } - } -} - -void VariantMapProperty::emitValueChangedRecursively(Property *property) -{ - emit property->valueChanged(); - - if (auto groupProperty = qobject_cast(property)) - for (auto subProperty : groupProperty->subProperties()) - emitValueChangedRecursively(subProperty); -} - -void VariantMapProperty::propertyTypesChanged() -{ - // When this happens in response to emitting value changed, it means we - // have triggered a change in a class definition. In this case we should - // not update ourselves. - if (mEmittingValueChanged) - return; - - QScopedValueRollback emittingValueChanged(mPropertyTypesChanged, true); - setValue(mValue, mSuggestions); -} - -void VariantMapProperty::emitMemberValueChanged(const QStringList &path, const QVariant &value) -{ - QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); - emit memberValueChanged(path, value); - emit valueChanged(); -} - -void VariantMapProperty::memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos) -{ - QMenu menu; - - // Add Expand All and Collapse All actions to group properties - if (auto groupProperty = qobject_cast(property)) { - menu.addAction(tr("Expand All"), groupProperty, &GroupProperty::expandAll); - menu.addAction(tr("Collapse All"), groupProperty, &GroupProperty::collapseAll); - } - - // Provide the Add, Remove and Reset actions also here - if (property->actions()) { - menu.addSeparator(); - - if (property->actions() & Property::Action::Add) { - QAction *add = menu.addAction(m_addIcon, tr("Add Property"), this, [this, name = path.first()] { - addMember(name, mSuggestions.value(name)); - }); - Utils::setThemeIcon(add, "add"); - } - if (property->actions() & Property::Action::Remove) { - QAction *remove = menu.addAction(m_removeIcon, tr("Remove Property"), this, [this, name = path.first()] { - removeMember(name); - }); - Utils::setThemeIcon(remove, "remove"); - - // If a property can be removed, it can also be renamed - menu.addAction(m_renameIcon, tr("Rename Property..."), this, [this, name = path.first()] { - emit renameRequested(name); - }); - } - if (property->actions() & Property::Action::Reset) { - QAction *reset = menu.addAction(m_resetIcon, tr("Reset Member"), this, [=] { - setClassMember(path, QVariant()); - emitValueChangedRecursively(property); - }); - reset->setEnabled(property->isModified()); - Utils::setThemeIcon(reset, "edit-clear"); - } - } - - // todo: Add "Convert" sub-menu - // todo: Add "Copy" and "Paste" actions - - if (!menu.isEmpty()) - menu.exec(globalPos); -} - - void CustomProperties::setDocument(Document *document) { if (mDocument == document) diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 1742f8c82f..7c2969be15 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -32,7 +32,7 @@ class Object; class CustomProperties; class Document; class ObjectProperties; -class VariantEditor; +class VariantEditorView; /** * The PropertiesWidget combines the PropertyBrowser with some controls that @@ -81,8 +81,7 @@ public slots: ObjectProperties *mPropertiesObject = nullptr; CustomProperties *mCustomProperties = nullptr; QMap mExpandedStates; - QScrollArea *mScrollArea; - VariantEditor *mPropertyBrowser; + VariantEditorView *mPropertyBrowser; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 741569715f..3bf358f9fb 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -772,7 +772,7 @@ void PropertyLabel::paintEvent(QPaintEvent *event) const int spacing = Utils::dpiScaled(3); const int branchIndicatorWidth = Utils::dpiScaled(14); - const int indent = branchIndicatorWidth * std::max(m_level - 1, 0); + const int indent = branchIndicatorWidth * m_level; QStyleOption branchOption; branchOption.initFrom(this); @@ -804,7 +804,7 @@ void PropertyLabel::updateContentMargins() const int spacing = Utils::dpiScaled(3); const int branchIndicatorWidth = Utils::dpiScaled(14); const int verticalSpacing = m_header ? spacing : 0; - const int indent = branchIndicatorWidth * std::max(m_level, 1); + const int indent = branchIndicatorWidth * (m_level + 1); if (isLeftToRight()) setContentsMargins(spacing + indent, verticalSpacing, spacing, verticalSpacing); diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 945519389f..38d030178c 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -23,7 +23,6 @@ #include "addpropertydialog.h" #include "colorbutton.h" -#include "custompropertieshelper.h" #include "objecttypes.h" #include "preferences.h" #include "project.h" @@ -32,6 +31,8 @@ #include "savefile.h" #include "session.h" #include "utils.h" +#include "varianteditor.h" +#include "variantmapproperty.h" #include #include @@ -50,9 +51,6 @@ #endif #include -#include -#include - namespace Tiled { static QToolBar *createSmallToolBar(QWidget *parent) @@ -224,8 +222,8 @@ PropertyTypesEditor::PropertyTypesEditor(QWidget *parent) this, &PropertyTypesEditor::openAddMemberDialog); connect(mRemoveMemberAction, &QAction::triggered, this, &PropertyTypesEditor::removeMember); - connect(mRenameMemberAction, &QAction::triggered, - this, &PropertyTypesEditor::renameMember); + // connect(mRenameMemberAction, &QAction::triggered, + // this, &PropertyTypesEditor::renameMember); connect(mPropertyTypesModel, &PropertyTypesModel::nameChanged, this, &PropertyTypesEditor::propertyTypeNameChanged); @@ -372,6 +370,7 @@ ClassPropertyType *PropertyTypesEditor::selectedClassPropertyType() const void PropertyTypesEditor::currentMemberItemChanged(QtBrowserItem *item) { + // todo: change based on selection in VariantEditor mRemoveMemberAction->setEnabled(item); mRenameMemberAction->setEnabled(item); } @@ -599,6 +598,8 @@ void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) void PropertyTypesEditor::editMember(const QString &name) { + // todo: VariantEditor needs to support focusing the widget of a specific property +#if 0 QtVariantProperty *property = mPropertiesHelper->property(name); if (!property) return; @@ -606,10 +607,13 @@ void PropertyTypesEditor::editMember(const QString &name) const QList propertyItems = mMembersView->items(property); if (!propertyItems.isEmpty()) mMembersView->editItem(propertyItems.first()); +#endif } void PropertyTypesEditor::removeMember() { + // todo: VariantEditor needs to support selection +#if 0 QtBrowserItem *item = mMembersView->currentItem(); if (!item) return; @@ -641,37 +645,27 @@ void PropertyTypesEditor::removeMember() static_cast(*propertyType).members.remove(name); applyPropertyTypes(); +#endif } -void PropertyTypesEditor::renameMember() +void PropertyTypesEditor::renameMember(const QString &name) { - QtBrowserItem *item = mMembersView->currentItem(); - if (!item) - return; - - const QString oldName = item->property()->propertyName(); - - QInputDialog *dialog = new QInputDialog(mMembersView); + QInputDialog *dialog = new QInputDialog(mMembersEditor); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(tr("Name:")); - dialog->setTextValue(oldName); + dialog->setTextValue(name); dialog->setWindowTitle(tr("Rename Member")); - connect(dialog, &QInputDialog::textValueSelected, this, &PropertyTypesEditor::renameMemberTo); + connect(dialog, &QInputDialog::textValueSelected, this, [=] (const QString &newName) { + renameMemberTo(name, newName); + }); + dialog->open(); } -void PropertyTypesEditor::renameMemberTo(const QString &name) +void PropertyTypesEditor::renameMemberTo(const QString &oldName, const QString &name) { - if (name.isEmpty()) - return; - - QtBrowserItem *item = mMembersView->currentItem(); - if (!item) - return; - - const QString oldName = item->property()->propertyName(); - if (oldName == name) + if (name.isEmpty() || oldName == name) return; auto propertyType = selectedPropertyType(); @@ -838,18 +832,7 @@ void PropertyTypesEditor::updateDetails() mDrawFillCheckBox->setChecked(classType.drawFill); updateClassUsageDetails(classType); - mPropertiesHelper->clear(); - - QMapIterator it(classType.members); - while (it.hasNext()) { - it.next(); - - const QString &name = it.key(); - const QVariant &value = it.value(); - - QtProperty *property = mPropertiesHelper->createProperty(name, value); - mMembersView->addProperty(property); - } + mMembersProperty->setValue(classType.members); break; } case PropertyType::PT_Enum: { @@ -902,8 +885,8 @@ void PropertyTypesEditor::setCurrentPropertyType(PropertyType::Type type) mCurrentPropertyType = type; - delete mPropertiesHelper; - mPropertiesHelper = nullptr; + delete mMembersProperty; + mMembersProperty = nullptr; while (mDetailsLayout->rowCount() > 0) mDetailsLayout->removeRow(0); @@ -913,7 +896,7 @@ void PropertyTypesEditor::setCurrentPropertyType(PropertyType::Type type) mStorageTypeComboBox = nullptr; mValuesAsFlagsCheckBox = nullptr; mValuesView = nullptr; - mMembersView = nullptr; + mMembersEditor = nullptr; mAddValueAction->setEnabled(type == PropertyType::PT_Enum); mAddMemberAction->setEnabled(type == PropertyType::PT_Class); @@ -956,14 +939,21 @@ void PropertyTypesEditor::addClassProperties() nameAndColor->addWidget(mColorButton); nameAndColor->addWidget(mDrawFillCheckBox); - mMembersView = new QtTreePropertyBrowser(this); - mPropertiesHelper = new CustomPropertiesHelper(mMembersView, this); + mMembersEditor = new VariantEditorView(this); - connect(mPropertiesHelper, &CustomPropertiesHelper::propertyMemberValueChanged, - this, &PropertyTypesEditor::memberValueChanged); + const auto margin = Utils::dpiScaled(3); + mMembersEditor->widget()->setContentsMargins(0, margin, 0, margin); - connect(mMembersView, &QtTreePropertyBrowser::currentItemChanged, - this, &PropertyTypesEditor::currentMemberItemChanged); + mMembersProperty = new VariantMapProperty(QString(), this); + mMembersEditor->addProperty(mMembersProperty); + + connect(mMembersProperty, &VariantMapProperty::valueChanged, + this, &PropertyTypesEditor::classMembersChanged); + connect(mMembersProperty, &VariantMapProperty::renameRequested, + this, &PropertyTypesEditor::renameMember); + + // connect(mMembersView, &QtTreePropertyBrowser::currentItemChanged, + // this, &PropertyTypesEditor::currentMemberItemChanged); mUseAsPropertyCheckBox = new QCheckBox(tr("Property value")); @@ -993,7 +983,7 @@ void PropertyTypesEditor::addClassProperties() auto membersWithToolBarLayout = new QVBoxLayout; membersWithToolBarLayout->setSpacing(0); membersWithToolBarLayout->setContentsMargins(0, 0, 0, 0); - membersWithToolBarLayout->addWidget(mMembersView); + membersWithToolBarLayout->addWidget(mMembersEditor); membersWithToolBarLayout->addWidget(membersToolBar); mDetailsLayout->addRow(tr("Name"), nameAndColor); @@ -1124,7 +1114,7 @@ void PropertyTypesEditor::setUsageFlags(int flags, bool value) } } -void PropertyTypesEditor::memberValueChanged(const QStringList &path, const QVariant &value) +void PropertyTypesEditor::classMembersChanged() { if (mUpdatingDetails) return; @@ -1133,18 +1123,7 @@ void PropertyTypesEditor::memberValueChanged(const QStringList &path, const QVar if (!classType) return; - if (!setPropertyMemberValue(classType->members, path, value)) - return; - - // When a nested property was changed, we need to update the value of the - // top-level property to match. - if (path.size() > 1) { - auto &topLevelName = path.first(); - if (auto property = mPropertiesHelper->property(topLevelName)) { - QScopedValueRollback updatingDetails(mUpdatingDetails, true); - property->setValue(mPropertiesHelper->toDisplayValue(classType->members.value(topLevelName))); - } - } + classType->members = mMembersProperty->value(); applyPropertyTypes(); } diff --git a/src/tiled/propertytypeseditor.h b/src/tiled/propertytypeseditor.h index 1f853b98ad..27870c253a 100644 --- a/src/tiled/propertytypeseditor.h +++ b/src/tiled/propertytypeseditor.h @@ -33,7 +33,6 @@ class QStringListModel; class QTreeView; class QtBrowserItem; -class QtTreePropertyBrowser; namespace Ui { class PropertyTypesEditor; @@ -42,8 +41,9 @@ class PropertyTypesEditor; namespace Tiled { class ColorButton; -class CustomPropertiesHelper; class PropertyTypesModel; +class VariantEditorView; +class VariantMapProperty; struct PropertyTypesFilter { @@ -106,8 +106,8 @@ class PropertyTypesEditor : public QDialog void addMember(const QString &name, const QVariant &value = QVariant()); void editMember(const QString &name); void removeMember(); - void renameMember(); - void renameMemberTo(const QString &name); + void renameMember(const QString &name); + void renameMemberTo(const QString &oldName, const QString &name); void importPropertyTypes(); void exportPropertyTypes(); @@ -119,7 +119,7 @@ class PropertyTypesEditor : public QDialog void colorChanged(const QColor &color); void setDrawFill(bool value); void setUsageFlags(int flags, bool value); - void memberValueChanged(const QStringList &path, const QVariant &value); + void classMembersChanged(); void retranslateUi(); @@ -144,8 +144,8 @@ class PropertyTypesEditor : public QDialog QCheckBox *mDrawFillCheckBox = nullptr; QCheckBox *mClassOfCheckBox = nullptr; QPushButton *mClassOfButton = nullptr; - QtTreePropertyBrowser *mMembersView = nullptr; - CustomPropertiesHelper *mPropertiesHelper = nullptr; + VariantEditorView *mMembersEditor = nullptr; + VariantMapProperty *mMembersProperty = nullptr; bool mSettingPrefPropertyTypes = false; bool mSettingName = false; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index f9f69ad1bf..c9f9839408 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -621,9 +621,9 @@ void VariantEditor::setLevel(int level) { m_level = level; - setBackgroundRole((m_level % 2) ? QPalette::AlternateBase - : QPalette::Base); - setAutoFillBackground(m_level > 1); + setBackgroundRole((m_level % 2) ? QPalette::Base + : QPalette::AlternateBase); + setAutoFillBackground(m_level > 0); } QLayout *VariantEditor::createPropertyLayout(Property *property) @@ -761,7 +761,7 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo widgets.children = new VariantEditor(this); if (widgets.label && widgets.label->isHeader()) widgets.children->setContentsMargins(0, halfSpacing, 0, halfSpacing); - if (groupProperty->displayMode() != Property::DisplayMode::ChildrenOnly) + if (groupProperty->displayMode() == Property::DisplayMode::Default) widgets.children->setLevel(m_level + 1); widgets.children->setEnabled(groupProperty->isEnabled()); for (auto property : groupProperty->subProperties()) @@ -973,6 +973,23 @@ Property *createVariantProperty(const QString &name, return nullptr; } +VariantEditorView::VariantEditorView(QWidget *parent) + : QScrollArea(parent) +{ + auto scrollWidget = new QWidget(this); + scrollWidget->setBackgroundRole(QPalette::AlternateBase); + scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); + + auto verticalLayout = new QVBoxLayout(scrollWidget); + m_editor = new VariantEditor(scrollWidget); + verticalLayout->addWidget(m_editor); + verticalLayout->addStretch(); + verticalLayout->setContentsMargins(QMargins()); + + setWidgetResizable(true); + setWidget(scrollWidget); +} + } // namespace Tiled #include "moc_varianteditor.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 08b6a6554c..d5abef9492 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -535,6 +535,23 @@ class VariantEditor : public QWidget int m_level = 0; }; +class VariantEditorView : public QScrollArea +{ + Q_OBJECT + +public: + explicit VariantEditorView(QWidget *parent = nullptr); + + void clear() + { m_editor->clear(); } + + void addProperty(Property *property) + { m_editor->addProperty(property); } + +private: + VariantEditor *m_editor; +}; + } // namespace Tiled Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::Property::Actions) diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp new file mode 100644 index 0000000000..0acec4a301 --- /dev/null +++ b/src/tiled/variantmapproperty.cpp @@ -0,0 +1,467 @@ +/* + * variantmapproperty.cpp + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "variantmapproperty.h" + +#include "mapdocument.h" +#include "objectrefedit.h" +#include "preferences.h" +#include "utils.h" +#include "variantpropertymanager.h" + +#include +#include + +namespace Tiled { + +class ObjectRefProperty : public PropertyTemplate +{ + Q_OBJECT + +public: + using PropertyTemplate::PropertyTemplate; + + QWidget *createEditor(QWidget *parent) override + { + auto editor = new ObjectRefEdit(parent); + auto syncEditor = [this, editor] { + const QSignalBlocker blocker(editor); + editor->setValue(value()); + }; + syncEditor(); + connect(this, &Property::valueChanged, editor, syncEditor); + connect(editor, &ObjectRefEdit::valueChanged, + this, [this](const DisplayObjectRef &value) { + setValue(value); + }); + return editor; + } +}; + + +VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) + : GroupProperty(name, parent) + , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) + , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) + , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) + , m_renameIcon(QIcon(QLatin1String(":/images/16/rename.png"))) +{ + m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); + m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); + + connect(Preferences::instance(), &Preferences::propertyTypesChanged, + this, &VariantMapProperty::propertyTypesChanged); +} + +void VariantMapProperty::setValue(const QVariantMap &value, + const QVariantMap &suggestions) +{ + QVariantMap oldProperties = mSuggestions; + mergeProperties(oldProperties, mValue); + + mValue = value; + mSuggestions = suggestions; + + QVariantMap newProperties = suggestions; + mergeProperties(newProperties, value); + + // First, delete all properties that are not in allProperties + for (auto it = mPropertyMap.begin(); it != mPropertyMap.end(); ) { + if (newProperties.contains(it.key())) { + ++it; + } else { + deleteProperty(it.value()); + it = mPropertyMap.erase(it); + } + } + + int index = 0; + + QMapIterator it(newProperties); + while (it.hasNext()) { + it.next(); + const QString &name = it.key(); + const auto &newValue = it.value(); + const auto oldValue = oldProperties.value(name); + + if (createOrUpdateProperty(index, name, oldValue, newValue)) + ++index; + } + + // Don't emit valueChanged when we're just recreating properties with custom types + if (mPropertyTypesChanged) + return; + + QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); + emit valueChanged(); +} + +static bool canReuseProperty(const QVariant &a, + const QVariant &b, + bool propertyTypesChanged) +{ + if (a.userType() != b.userType()) + return false; + + // Two PropertyValue values might still have different types + if (a.userType() == propertyValueId()) { + // Trigger re-creation of the property when the types have changed + if (propertyTypesChanged) + return false; + + auto aTypeId = a.value().typeId; + auto bTypeId = b.value().typeId; + if (aTypeId != bTypeId) + return false; + } + + return true; +} + +bool VariantMapProperty::createOrUpdateProperty(int index, + const QString &name, + const QVariant &oldValue, + const QVariant &newValue) +{ + auto property = mPropertyMap.value(name); + + // If it already exists, check whether we need to delete it + if (property && !canReuseProperty(oldValue, newValue, mPropertyTypesChanged)) { + deleteProperty(property); + mPropertyMap.remove(name); + property = nullptr; + } + + // Try to create the property if necessary + if (!property) { + auto get = [=] { + return mValue.value(name, mSuggestions.value(name)); + }; + auto set = [=] (const QVariant &value) { + mValue.insert(name, value); + emitMemberValueChanged({ name }, value); + }; + + property = createProperty({ name }, std::move(get), std::move(set)); + if (property) { + connect(property, &Property::removeRequested, this, [=] { + removeMember(name); + }); + + connect(property, &Property::addRequested, this, [=] { + addMember(name, mSuggestions.value(name)); + }); + + insertProperty(index, property); + mPropertyMap.insert(name, property); + } else { + qWarning() << "Failed to create property for" << name + << "with type" << newValue.typeName(); + } + } + + if (property) { + if (mValue.contains(name)) { + property->setEnabled(true); + property->setActions(Property::Action::Remove); + } else { + property->setEnabled(false); + property->setActions(Property::Action::Add); + } + + updateModifiedRecursively(property, newValue); + emitValueChangedRecursively(property); + } + + return true; +} + +Property *VariantMapProperty::createProperty(const QStringList &path, + std::function get, + std::function set) +{ + Property *property = nullptr; + + const auto value = get(); + const auto type = value.userType(); + const auto &name = path.last(); + QString typeName; + + if (type == filePathTypeId()) { + auto getUrl = [get = std::move(get)] { return get().value().url; }; + auto setUrl = [set = std::move(set)] (const QUrl &value) { + set(QVariant::fromValue(FilePath { value })); + }; + property = new UrlProperty(name, std::move(getUrl), std::move(setUrl)); + } else if (type == objectRefTypeId()) { + auto getObjectRef = [get = std::move(get), this] { + return DisplayObjectRef(get().value(), + static_cast(mDocument)); // todo: shouldn't it be qobject_cast? + }; + auto setObjectRef = [set = std::move(set)](const DisplayObjectRef &value) { + set(QVariant::fromValue(value.ref)); + }; + property = new ObjectRefProperty(name, std::move(getObjectRef), std::move(setObjectRef)); + } else if (type == propertyValueId()) { + const auto propertyValue = value.value(); + if (auto propertyType = propertyValue.type()) { + switch (propertyType->type) { + case PropertyType::PT_Invalid: + break; + case PropertyType::PT_Class: { + auto classType = static_cast(*propertyType); + + auto groupProperty = new GroupProperty(name); + groupProperty->setHeader(false); + + createClassMembers(path, groupProperty, classType, std::move(get)); + + groupProperty->setExpanded(mExpandedProperties.contains(path)); + + connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { + if (expanded) + mExpandedProperties.insert(path); + else + mExpandedProperties.remove(path); + }); + + property = groupProperty; + break; + } + case PropertyType::PT_Enum: { + auto enumProperty = new BaseEnumProperty( + name, + [get = std::move(get)] { return get().value().value.toInt(); }, + [set = std::move(set), propertyType](int value) { + set(propertyType->wrap(value)); + }); + + auto enumType = static_cast(*propertyType); + enumProperty->setEnumData(enumType.values); + enumProperty->setFlags(enumType.valuesAsFlags); + + property = enumProperty; + break; + } + } + + typeName = propertyType->name; + } else { + typeName = tr("Unknown type"); + } + } else { + property = createVariantProperty(name, std::move(get), std::move(set)); + } + + if (property) { + if (typeName.isEmpty()) + typeName = typeToName(type); + + property->setToolTip(QStringLiteral("%1 : %2") + .arg(property->name(), typeName)); + + connect(property, &Property::contextMenuRequested, this, [=](const QPoint &globalPos) { + memberContextMenuRequested(property, path, globalPos); + }); + } + + return property; +} + +void VariantMapProperty::createClassMembers(const QStringList &path, + GroupProperty *groupProperty, + const ClassPropertyType &classType, + std::function get) +{ + // Create a sub-property for each member + QMapIterator it(classType.members); + while (it.hasNext()) { + it.next(); + const QString &name = it.key(); + + auto childPath = path; + childPath.append(name); + + auto getMember = [=] { + auto def = classType.members.value(name); + return get().value().value.toMap().value(name, def); + }; + auto setMember = [=] (const QVariant &value) { + setClassMember(childPath, value); + }; + + if (auto childProperty = createProperty(childPath, std::move(getMember), setMember)) { + childProperty->setActions(Property::Action::Reset); + groupProperty->addProperty(childProperty); + + connect(childProperty, &Property::resetRequested, this, [=] { + setMember(QVariant()); + emitValueChangedRecursively(childProperty); + }); + } + } +} + +void VariantMapProperty::removeMember(const QString &name) +{ + if (!mValue.contains(name)) + return; + + const auto oldValue = mValue.take(name); + + if (!mSuggestions.contains(name)) { + if (auto property = mPropertyMap.take(name)) + deleteProperty(property); + } else if (auto property = mPropertyMap.value(name)) { + const auto newValue = mSuggestions.value(name); + const int index = indexOfProperty(property); + createOrUpdateProperty(index, name, oldValue, newValue); + } + + emitMemberValueChanged({ name }, QVariant()); +} + +void VariantMapProperty::addMember(const QString &name, const QVariant &value) +{ + const auto oldValue = mValue.value(name, mSuggestions.value(name)); + + mValue.insert(name, value); + + if (auto property = mPropertyMap.value(name)) { + const int index = indexOfProperty(property); + createOrUpdateProperty(index, name, oldValue, value); + } + + emitMemberValueChanged({ name }, value); +} + +void VariantMapProperty::setClassMember(const QStringList &path, const QVariant &value) +{ + if (!setPropertyMemberValue(mValue, path, value)) + return; + + const auto &topLevelName = path.first(); + updateModifiedRecursively(mPropertyMap.value(topLevelName), + mValue.value(topLevelName)); + + emitMemberValueChanged(path, value); +} + +void VariantMapProperty::updateModifiedRecursively(Property *property, + const QVariant &value) +{ + auto groupProperty = qobject_cast(property); + if (!groupProperty) + return; + + const QVariantMap classValue = value.value().value.toMap(); + + for (auto subProperty : groupProperty->subProperties()) { + const auto &name = subProperty->name(); + const bool isModified = classValue.contains(name); + + if (subProperty->isModified() != isModified || subProperty->isModified()) { + subProperty->setModified(isModified); + updateModifiedRecursively(subProperty, classValue.value(name)); + } + } +} + +void VariantMapProperty::emitValueChangedRecursively(Property *property) +{ + emit property->valueChanged(); + + if (auto groupProperty = qobject_cast(property)) + for (auto subProperty : groupProperty->subProperties()) + emitValueChangedRecursively(subProperty); +} + +void VariantMapProperty::propertyTypesChanged() +{ + // When this happens in response to emitting value changed, it means we + // have triggered a change in a class definition. In this case we should + // not update ourselves. + if (mEmittingValueChanged) + return; + + QScopedValueRollback propertyTypesChanged(mPropertyTypesChanged, true); + setValue(mValue, mSuggestions); +} + +void VariantMapProperty::emitMemberValueChanged(const QStringList &path, const QVariant &value) +{ + QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); + emit memberValueChanged(path, value); + emit valueChanged(); +} + +void VariantMapProperty::memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos) +{ + QMenu menu; + + // Add Expand All and Collapse All actions to group properties + if (auto groupProperty = qobject_cast(property)) { + menu.addAction(tr("Expand All"), groupProperty, &GroupProperty::expandAll); + menu.addAction(tr("Collapse All"), groupProperty, &GroupProperty::collapseAll); + } + + // Provide the Add, Remove and Reset actions also here + if (property->actions()) { + menu.addSeparator(); + + if (property->actions() & Property::Action::Add) { + QAction *add = menu.addAction(m_addIcon, tr("Add Property"), this, [this, name = path.first()] { + addMember(name, mSuggestions.value(name)); + }); + Utils::setThemeIcon(add, "add"); + } + if (property->actions() & Property::Action::Remove) { + QAction *remove = menu.addAction(m_removeIcon, tr("Remove Property"), this, [this, name = path.first()] { + removeMember(name); + }); + Utils::setThemeIcon(remove, "remove"); + + // If a property can be removed, it can also be renamed + menu.addAction(m_renameIcon, tr("Rename Property..."), this, [this, name = path.first()] { + emit renameRequested(name); + }); + } + if (property->actions() & Property::Action::Reset) { + QAction *reset = menu.addAction(m_resetIcon, tr("Reset Member"), this, [=] { + setClassMember(path, QVariant()); + emitValueChangedRecursively(property); + }); + reset->setEnabled(property->isModified()); + Utils::setThemeIcon(reset, "edit-clear"); + } + } + + // todo: Add "Convert" sub-menu + // todo: Add "Copy" and "Paste" actions + + if (!menu.isEmpty()) + menu.exec(globalPos); +} + +} // namespace Tiled + +#include "moc_variantmapproperty.cpp" +#include "variantmapproperty.moc" diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h new file mode 100644 index 0000000000..5219036f97 --- /dev/null +++ b/src/tiled/variantmapproperty.h @@ -0,0 +1,89 @@ +/* + * variantmapproperty.h + * Copyright 2024, Thorbjørn Lindeijer + * + * This file is part of Tiled. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include "propertytype.h" +#include "varianteditor.h" + +namespace Tiled { + +class Document; + +class VariantMapProperty : public GroupProperty +{ + Q_OBJECT + +public: + VariantMapProperty(const QString &name, QObject *parent = nullptr); + + void setValue(const QVariantMap &value, + const QVariantMap &suggestions = {}); + + const QVariantMap &value() const { return mValue; } + +signals: + void memberValueChanged(const QStringList &path, const QVariant &value); + void renameRequested(const QString &name); + +protected: + virtual void propertyTypesChanged(); + + Document *mDocument = nullptr; + bool mPropertyTypesChanged = false; + +private: + bool createOrUpdateProperty(int index, + const QString &name, + const QVariant &oldValue, + const QVariant &newValue); + Property *createProperty(const QStringList &path, + std::function get, + std::function set); + + void createClassMembers(const QStringList &path, + GroupProperty *groupProperty, + const ClassPropertyType &classType, + std::function get); + + void removeMember(const QString &name); + void addMember(const QString &name, const QVariant &value); + void setClassMember(const QStringList &path, const QVariant &value); + + void updateModifiedRecursively(Property *property, const QVariant &value); + void emitValueChangedRecursively(Property *property); + + void emitMemberValueChanged(const QStringList &path, const QVariant &value); + + void memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos); + + QIcon m_resetIcon; + QIcon m_removeIcon; + QIcon m_addIcon; + QIcon m_renameIcon; + bool mEmittingValueChanged = false; + QVariantMap mValue; + QVariantMap mSuggestions; + QHash mPropertyMap; + QSet mExpandedProperties; +}; + + +} // namespace Tiled From 2b710d4dd6f0a8e8c945afb89a50548a1454556c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 8 Nov 2024 16:13:40 +0100 Subject: [PATCH 59/78] Removed now unused PropertyBrowser and CustomPropertiesHelper --- src/tiled/custompropertieshelper.cpp | 351 ---- src/tiled/custompropertieshelper.h | 108 -- src/tiled/libtilededitor.qbs | 4 - src/tiled/propertieswidget.cpp | 3 +- src/tiled/propertybrowser.cpp | 2240 -------------------------- src/tiled/propertybrowser.h | 259 --- 6 files changed, 1 insertion(+), 2964 deletions(-) delete mode 100644 src/tiled/custompropertieshelper.cpp delete mode 100644 src/tiled/custompropertieshelper.h delete mode 100644 src/tiled/propertybrowser.cpp delete mode 100644 src/tiled/propertybrowser.h diff --git a/src/tiled/custompropertieshelper.cpp b/src/tiled/custompropertieshelper.cpp deleted file mode 100644 index a64dd7ed3a..0000000000 --- a/src/tiled/custompropertieshelper.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* - * custompropertieshelper.cpp - * Copyright 2021, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "custompropertieshelper.h" - -#include "object.h" -#include "preferences.h" -#include "propertytype.h" -#include "varianteditorfactory.h" -#include "variantpropertymanager.h" - -#include -#include - -namespace Tiled { - -CustomPropertiesHelper::CustomPropertiesHelper(QtAbstractPropertyBrowser *propertyBrowser, - QObject *parent) - : QObject(parent) - , mPropertyBrowser(propertyBrowser) - , mPropertyManager(new VariantPropertyManager(this)) -{ - auto variantEditorFactory = new VariantEditorFactory(this); - - propertyBrowser->setFactoryForManager(mPropertyManager, variantEditorFactory); - - connect(mPropertyManager, &QtVariantPropertyManager::valueChanged, - this, &CustomPropertiesHelper::onValueChanged); - - connect(variantEditorFactory, &VariantEditorFactory::resetProperty, - this, &CustomPropertiesHelper::resetProperty); - - connect(Preferences::instance(), &Preferences::propertyTypesChanged, - this, &CustomPropertiesHelper::propertyTypesChanged); -} - -CustomPropertiesHelper::~CustomPropertiesHelper() -{ - mPropertyBrowser->unsetFactoryForManager(mPropertyManager); -} - -QtVariantProperty *CustomPropertiesHelper::createProperty(const QString &name, - const QVariant &value) -{ - Q_ASSERT(!mProperties.contains(name)); - - QScopedValueRollback updating(mUpdating, true); - - QtVariantProperty *property = createPropertyInternal(name, value); - property->setValue(toDisplayValue(value)); - - mProperties.insert(name, property); - - return property; -} - -QtVariantProperty *CustomPropertiesHelper::createPropertyInternal(const QString &name, - const QVariant &value) -{ - int type = value.userType(); - - const PropertyType *propertyType = nullptr; - - if (type == propertyValueId()) { - const PropertyValue propertyValue = value.value(); - propertyType = propertyValue.type(); - - if (propertyType) { - switch (propertyType->type) { - case PropertyType::PT_Invalid: - break; - case PropertyType::PT_Class: - type = VariantPropertyManager::unstyledGroupTypeId(); - break; - case PropertyType::PT_Enum: { - const auto &enumType = static_cast(*propertyType); - if (enumType.valuesAsFlags) - type = QtVariantPropertyManager::flagTypeId(); - else - type = QtVariantPropertyManager::enumTypeId(); - break; - } - } - } - } - - if (type == objectRefTypeId()) - type = VariantPropertyManager::displayObjectRefTypeId(); - - QtVariantProperty *property = mPropertyManager->addProperty(type, name); - if (!property) { - // fall back to string property for unsupported property types - property = mPropertyManager->addProperty(QMetaType::QString, name); - } - - if (type == QMetaType::Bool) - property->setAttribute(QLatin1String("textVisible"), false); - if (type == QMetaType::QString) - property->setAttribute(QLatin1String("multiline"), true); - if (type == QMetaType::Double) - property->setAttribute(QLatin1String("decimals"), 9); - - if (propertyType) { - mPropertyTypeIds.insert(property, propertyType->id); - setPropertyAttributes(property, *propertyType); - } else { - mPropertyTypeIds.insert(property, 0); - } - - return property; -} - -void CustomPropertiesHelper::deleteProperty(QtProperty *property) -{ - Q_ASSERT(hasProperty(property)); - - mProperties.remove(property->propertyName()); - deletePropertyInternal(property); -} - -void CustomPropertiesHelper::deletePropertyInternal(QtProperty *property) -{ - Q_ASSERT(mPropertyTypeIds.contains(property)); - deleteSubProperties(property); - mPropertyTypeIds.remove(property); - delete property; -} - -void CustomPropertiesHelper::deleteSubProperties(QtProperty *property) -{ - const auto subProperties = property->subProperties(); - for (QtProperty *subProperty : subProperties) { - if (mPropertyParents.value(subProperty) == property) { - deletePropertyInternal(subProperty); - mPropertyParents.remove(subProperty); - } - } -} - -void CustomPropertiesHelper::clear() -{ - QHashIterator it(mPropertyTypeIds); - while (it.hasNext()) - delete it.next().key(); - - mProperties.clear(); - mPropertyTypeIds.clear(); - mPropertyParents.clear(); -} - -QVariant CustomPropertiesHelper::toDisplayValue(QVariant value) const -{ - if (value.userType() == propertyValueId()) - value = value.value().value; - - if (value.userType() == objectRefTypeId()) - value = QVariant::fromValue(DisplayObjectRef { value.value(), mMapDocument }); - - return value; -} - -QVariant CustomPropertiesHelper::fromDisplayValue(QtProperty *property, - QVariant value) const -{ - if (value.userType() == VariantPropertyManager::displayObjectRefTypeId()) - value = QVariant::fromValue(value.value().ref); - - if (auto type = propertyType(property)) - value = type->wrap(value); - - return value; -} - -void CustomPropertiesHelper::onValueChanged(QtProperty *property, const QVariant &value) -{ - if (!mPropertyTypeIds.contains(property)) - return; - - if (!mUpdating) { - const auto propertyValue = fromDisplayValue(property, value); - const auto path = propertyPath(property); - - QScopedValueRollback emittingValueChanged(mEmittingValueChanged, true); - emit propertyMemberValueChanged(path, propertyValue); - } - - if (auto type = propertyType(property); type && type->isClass()) { - // Apply the change to the children - - auto &members = static_cast(*type).members; - - const auto subProperties = property->subProperties(); - const auto map = value.toMap(); - - QScopedValueRollback updating(mUpdating, true); - - for (QtProperty *subProperty : subProperties) { - const auto name = subProperty->propertyName(); - const bool modified = map.contains(name); - const auto value = modified ? map.value(name) - : members.value(name); - - // Avoid setting child class members as modified, just because - // the class definition sets different defaults on them. - const bool isParentTopLevel = !mPropertyParents.contains(property); - const bool isParentModified = property->isModified(); - subProperty->setModified(modified && (isParentTopLevel || isParentModified)); - - static_cast(subProperty)->setValue(toDisplayValue(value)); - } - } -} - -void CustomPropertiesHelper::resetProperty(QtProperty *property) -{ - // Reset class property value by removing it - if (property->isModified()) { - // Only nested properties are currently marked as "modified", so in - // this case we rely on the handling of this signal - emit propertyMemberValueChanged(propertyPath(property), QVariant()); - return; - } - - // Some other type can reset their value - auto typeId = mPropertyManager->propertyType(property); - - if (typeId == QMetaType::QColor) - mPropertyManager->setValue(property, QColor()); - else if (typeId == VariantPropertyManager::displayObjectRefTypeId()) { - mPropertyManager->setValue(property, toDisplayValue(QVariant::fromValue(ObjectRef()))); - } else { - qWarning() << "Requested reset of unsupported type" << typeId << "for property" << property->propertyName(); - } -} - -void CustomPropertiesHelper::propertyTypesChanged() -{ - // When this happens in response to emitting propertyValueChanged, it means - // we have triggered a change in a class definition. In this case we should - // not update ourselves. - if (mEmittingValueChanged) - return; - - QHashIterator it(mProperties); - while (it.hasNext()) { - it.next(); - const auto property = it.value(); - const auto typeId = mPropertyTypeIds.value(property); - if (!typeId) - continue; - - if (const auto type = Object::propertyTypes().findTypeById(typeId)) { - setPropertyAttributes(property, *type); - - if (type->isClass()) { - // Restore the existing member values - QScopedValueRollback updating(mUpdating, true); - onValueChanged(property, property->value()); - } - } - } -} - -void CustomPropertiesHelper::setPropertyAttributes(QtVariantProperty *property, - const PropertyType &propertyType) -{ - switch (propertyType.type) { - case Tiled::PropertyType::PT_Invalid: - break; - case Tiled::PropertyType::PT_Class: { - const auto &classType = static_cast(propertyType); - - // Delete any existing sub-properties - deleteSubProperties(property); - - // Create a sub-property for each member - QMapIterator it(classType.members); - while (it.hasNext()) { - it.next(); - const QString &name = it.key(); - const QVariant &value = it.value(); - - auto subProperty = createPropertyInternal(name, value); - property->addSubProperty(subProperty); - mPropertyParents.insert(subProperty, property); - } - break; - } - case Tiled::PropertyType::PT_Enum: { - const auto &enumType = static_cast(propertyType); - const bool isFlags = property->propertyType() == QtVariantPropertyManager::flagTypeId(); - - // Need to re-create the property when valuesAsFlags changed, but we - // don't have access to the view. - if (enumType.valuesAsFlags != isFlags) { - emit recreateProperty(property, fromDisplayValue(property, property->value())); - return; - } - - // Setting these attributes leads to emission of valueChanged... - QScopedValueRollback updating(mUpdating, true); - - if (enumType.valuesAsFlags) { - mPropertyManager->setAttribute(property, QStringLiteral("flagNames"), enumType.values); - } else { - // TODO: Support icons for enum values - mPropertyManager->setAttribute(property, QStringLiteral("enumNames"), enumType.values); - } - break; - } - } -} - -const PropertyType *CustomPropertiesHelper::propertyType(QtProperty *property) const -{ - if (const auto typeId = mPropertyTypeIds.value(property)) - return Object::propertyTypes().findTypeById(typeId); - return nullptr; -} - -QStringList CustomPropertiesHelper::propertyPath(QtProperty *property) const -{ - QStringList path; - - if (auto parent = mPropertyParents.value(property)) - path = propertyPath(parent); - - path.append(property->propertyName()); - return path; -} - -} // namespace Tiled - -#include "moc_custompropertieshelper.cpp" diff --git a/src/tiled/custompropertieshelper.h b/src/tiled/custompropertieshelper.h deleted file mode 100644 index 1006b4a071..0000000000 --- a/src/tiled/custompropertieshelper.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * custompropertieshelper.h - * Copyright 2021, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once - -#include -#include - -class QtAbstractPropertyBrowser; -class QtProperty; -class QtVariantProperty; -class QtVariantPropertyManager; - -namespace Tiled { - -class MapDocument; -class PropertyType; -class VariantEditorFactory; - -class CustomPropertiesHelper : public QObject -{ - Q_OBJECT - -public: - CustomPropertiesHelper(QtAbstractPropertyBrowser *propertyBrowser, - QObject *parent = nullptr); - ~CustomPropertiesHelper() override; - - QtVariantProperty *createProperty(const QString &name, const QVariant &value); - void deleteProperty(QtProperty *property); - void clear(); - bool hasProperty(QtProperty *property) const; - QtVariantProperty *property(const QString &name); - const QHash &properties() const; - - QVariant toDisplayValue(QVariant value) const; - QVariant fromDisplayValue(QtProperty *property, QVariant value) const; - - void setMapDocument(MapDocument *mapDocument); - -signals: - void propertyMemberValueChanged(const QStringList &path, const QVariant &value); - void recreateProperty(QtVariantProperty *property, const QVariant &value); - -private: - QtVariantProperty *createPropertyInternal(const QString &name, - const QVariant &value); - void deletePropertyInternal(QtProperty *property); - void deleteSubProperties(QtProperty *property); - - void onValueChanged(QtProperty *property, const QVariant &value); - void resetProperty(QtProperty *property); - void propertyTypesChanged(); - - void setPropertyAttributes(QtVariantProperty *property, - const PropertyType &propertyType); - - const PropertyType *propertyType(QtProperty *property) const; - QStringList propertyPath(QtProperty *property) const; - - QtAbstractPropertyBrowser *mPropertyBrowser; - QtVariantPropertyManager *mPropertyManager; - MapDocument *mMapDocument = nullptr; - QHash mProperties; - QHash mPropertyTypeIds; - QHash mPropertyParents; - bool mUpdating = false; - bool mEmittingValueChanged = false; -}; - -inline bool CustomPropertiesHelper::hasProperty(QtProperty *property) const -{ - return mPropertyTypeIds.contains(property) && !mPropertyParents.contains(property); -} - -inline QtVariantProperty *CustomPropertiesHelper::property(const QString &name) -{ - return mProperties.value(name); -} - -inline const QHash &CustomPropertiesHelper::properties() const -{ - return mProperties; -} - -inline void CustomPropertiesHelper::setMapDocument(MapDocument *mapDocument) -{ - mMapDocument = mapDocument; -} - -} // namespace Tiled diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 6e096753af..15ddd80f1c 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -199,8 +199,6 @@ DynamicLibrary { "createtextobjecttool.h", "createtileobjecttool.cpp", "createtileobjecttool.h", - "custompropertieshelper.cpp", - "custompropertieshelper.h", "debugdrawitem.cpp", "debugdrawitem.h", "document.cpp", @@ -404,8 +402,6 @@ DynamicLibrary { "propertiesdock.h", "propertieswidget.cpp", "propertieswidget.h", - "propertybrowser.cpp", - "propertybrowser.h", "propertyeditorwidgets.cpp", "propertyeditorwidgets.h", "propertytypeseditor.cpp", diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index cc02247b15..6275a4b66b 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -38,7 +38,6 @@ #include "objectgroup.h" #include "objecttemplate.h" #include "preferences.h" -#include "propertybrowser.h" #include "propertyeditorwidgets.h" #include "tilesetchanges.h" #include "tilesetdocument.h" @@ -2202,7 +2201,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) setLayout(layout); mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); - connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, + connect(mPropertyBrowser, &QWidget::customContextMenuRequested, this, &PropertiesWidget::showContextMenu); // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, // this, &PropertiesWidget::updateActions); diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp deleted file mode 100644 index 13f31df60b..0000000000 --- a/src/tiled/propertybrowser.cpp +++ /dev/null @@ -1,2240 +0,0 @@ -/* - * propertybrowser.cpp - * Copyright 2013-2022, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "propertybrowser.h" - -#include "changeimagelayerproperty.h" -#include "changelayer.h" -#include "changemapobject.h" -#include "changemapproperty.h" -#include "changeobjectgroupproperties.h" -#include "changeproperties.h" -#include "changetile.h" -#include "changetileimagesource.h" -#include "changewangcolordata.h" -#include "changewangsetdata.h" -#include "compression.h" -#include "documentmanager.h" -#include "flipmapobjects.h" -#include "grouplayer.h" -#include "imagelayer.h" -#include "map.h" -#include "mapdocument.h" -#include "mapobject.h" -#include "objectgroup.h" -#include "objecttemplate.h" -#include "preferences.h" -#include "properties.h" -#include "replacetileset.h" -#include "stylehelper.h" -#include "tile.h" -#include "tilelayer.h" -#include "tilesetchanges.h" -#include "tilesetdocument.h" -#include "tilesetformat.h" -#include "tilesetmanager.h" -#include "tilesetwangsetmodel.h" -#include "utils.h" -#include "varianteditorfactory.h" -#include "variantpropertymanager.h" -#include "wangcolormodel.h" -#include "wangoverlay.h" -#include "wangset.h" - -#include - -#include -#include -#include -#include -#include - -#include - -namespace Tiled { - -namespace { - -/** - * A helper class instantiated while properties are updated. - * - * Makes sure the resize mode is set to Fixed during its lifetime. Used to work - * around performance issues caused by the view continuously making sure its - * name column is adjusted to the contents. - * - * Also restores the scroll position, which gets reset when properties are - * removed and then added back. - */ -class UpdatingProperties -{ -public: - UpdatingProperties(QtTreePropertyBrowser *browser, bool &isUpdating, bool force = false) - : mBrowser(browser) - , mForced(force) - , mWasUpdating(isUpdating) - , mIsUpdating(isUpdating) - { - if (!isUpdating || force) { - isUpdating = true; - mPreviousResizeMode = browser->resizeMode(); - mPreviousScrollPosition = browser->scrollPosition(); - mBrowser->setResizeMode(QtTreePropertyBrowser::Fixed); - } - } - - ~UpdatingProperties() - { - if (!mWasUpdating || mForced) { - mBrowser->setResizeMode(mPreviousResizeMode); - mBrowser->setScrollPosition(mPreviousScrollPosition); - mIsUpdating = mWasUpdating; - } - } - -private: - QtTreePropertyBrowser * const mBrowser; - bool const mForced; - bool const mWasUpdating; - bool &mIsUpdating; - QtTreePropertyBrowser::ResizeMode mPreviousResizeMode; - int mPreviousScrollPosition; -}; - -} // anonymous namespace - -PropertyBrowser::PropertyBrowser(QWidget *parent) - : QtTreePropertyBrowser(parent) - , mVariantManager(new VariantPropertyManager(this)) - , mGroupManager(new QtGroupPropertyManager(this)) - , mCustomPropertiesGroup(nullptr) - , mCustomPropertiesHelper(this) -{ - VariantEditorFactory *variantEditorFactory = new VariantEditorFactory(this); - - setFactoryForManager(mVariantManager, variantEditorFactory); - setResizeMode(ResizeToContents); - setRootIsDecorated(false); - setPropertiesWithoutValueMarked(true); - setAllowMultiSelection(true); - - retranslateUi(); - - mWangSetIcons.insert(WangSet::Corner, wangSetIcon(WangSet::Corner)); - mWangSetIcons.insert(WangSet::Edge, wangSetIcon(WangSet::Edge)); - mWangSetIcons.insert(WangSet::Mixed, wangSetIcon(WangSet::Mixed)); - - connect(mVariantManager, &QtVariantPropertyManager::valueChanged, - this, &PropertyBrowser::valueChanged); - - connect(&mCustomPropertiesHelper, &CustomPropertiesHelper::propertyMemberValueChanged, - this, &PropertyBrowser::customPropertyValueChanged); - - connect(&mCustomPropertiesHelper, &CustomPropertiesHelper::recreateProperty, - this, &PropertyBrowser::recreateProperty); - - connect(variantEditorFactory, &VariantEditorFactory::resetProperty, - this, &PropertyBrowser::resetProperty); - - connect(Preferences::instance(), &Preferences::propertyTypesChanged, - this, &PropertyBrowser::propertyTypesChanged); - - connect(StyleHelper::instance(), &StyleHelper::styleApplied, - this, &PropertyBrowser::updateCustomPropertyColors); -} - -/** - * Sets the \a object for which to display the properties. - */ -void PropertyBrowser::setObject(Object *object) -{ - if (mObject == object) - return; - - UpdatingProperties updatingProperties(this, mUpdating); - removeProperties(); - mObject = object; - addProperties(); -} - -/** - * Sets the \a document, used for keeping track of changes and for - * undo/redo support. - */ -void PropertyBrowser::setDocument(Document *document) -{ - MapDocument *mapDocument = qobject_cast(document); - TilesetDocument *tilesetDocument = qobject_cast(document); - - if (mDocument == document) - return; - - if (mDocument) { - mDocument->disconnect(this); - if (mTilesetDocument) { - mTilesetDocument->wangSetModel()->disconnect(this); - } - } - - mDocument = document; - mMapDocument = mapDocument; - mTilesetDocument = tilesetDocument; - mCustomPropertiesHelper.setMapDocument(mapDocument); - - if (mapDocument) { - connect(mapDocument, &MapDocument::mapChanged, - this, &PropertyBrowser::mapChanged); - - connect(mapDocument, &MapDocument::selectedObjectsChanged, - this, &PropertyBrowser::selectedObjectsChanged); - connect(mapDocument, &MapDocument::selectedLayersChanged, - this, &PropertyBrowser::selectedLayersChanged); - } - - if (tilesetDocument) { - connect(tilesetDocument, &TilesetDocument::tilesetNameChanged, - this, &PropertyBrowser::tilesetChanged); - connect(tilesetDocument, &TilesetDocument::tilesetTileOffsetChanged, - this, &PropertyBrowser::tilesetChanged); - connect(tilesetDocument, &TilesetDocument::tilesetObjectAlignmentChanged, - this, &PropertyBrowser::tilesetChanged); - connect(tilesetDocument, &TilesetDocument::tilesetChanged, - this, &PropertyBrowser::tilesetChanged); - - connect(tilesetDocument, &TilesetDocument::tileProbabilityChanged, - this, &PropertyBrowser::tileChanged); - connect(tilesetDocument, &TilesetDocument::tileImageSourceChanged, - this, &PropertyBrowser::tileChanged); - - connect(tilesetDocument, &TilesetDocument::selectedTilesChanged, - this, &PropertyBrowser::selectedTilesChanged); - - TilesetWangSetModel *wangSetModel = tilesetDocument->wangSetModel(); - connect(wangSetModel, &TilesetWangSetModel::wangSetChanged, - this, &PropertyBrowser::wangSetChanged); - } - - if (document) { - connect(document, &Document::changed, - this, &PropertyBrowser::documentChanged); - - // For custom properties: - connect(document, &Document::propertyAdded, - this, &PropertyBrowser::propertyAdded); - connect(document, &Document::propertyRemoved, - this, &PropertyBrowser::propertyRemoved); - connect(document, &Document::propertyChanged, - this, &PropertyBrowser::propertyChanged); - connect(document, &Document::propertiesChanged, - this, &PropertyBrowser::propertiesChanged); - } -} - -/** - * Returns whether the given \a item displays a custom property. - */ -bool PropertyBrowser::isCustomPropertyItem(const QtBrowserItem *item) const -{ - return mCustomPropertiesHelper.hasProperty(item->property()); -} - -/** - * Returns whether the given list of \a items are all custom properties. - */ -bool PropertyBrowser::allCustomPropertyItems(const QList &items) const -{ - return std::all_of(items.begin(), items.end(), [this] (QtBrowserItem *item) { - return isCustomPropertyItem(item); - }); -} - -/** - * Selects the custom property with the given \a name, if it exists. - */ -void PropertyBrowser::selectCustomProperty(const QString &name) -{ - QtVariantProperty *property = mCustomPropertiesHelper.property(name); - if (!property) - return; - - const QList propertyItems = items(property); - if (!propertyItems.isEmpty()) - setCurrentItem(propertyItems.first()); -} - -/** - * Makes the custom property with the \a name the currently edited one, - * if it exists. - */ -void PropertyBrowser::editCustomProperty(const QString &name) -{ - QtVariantProperty *property = mCustomPropertiesHelper.property(name); - if (!property) - return; - - const QList propertyItems = items(property); - if (!propertyItems.isEmpty()) - editItem(propertyItems.first()); -} - -QSize PropertyBrowser::sizeHint() const -{ - return Utils::dpiScaled(QSize(260, 100)); -} - -bool PropertyBrowser::event(QEvent *event) -{ - if (event->type() == QEvent::LanguageChange) - retranslateUi(); - - if (event->type() == QEvent::ShortcutOverride) { - if (static_cast(event)->key() == Qt::Key_Tab) { - if (editedItem()) { - event->accept(); - return true; - } - } - } - - return QtTreePropertyBrowser::event(event); -} - -void PropertyBrowser::documentChanged(const ChangeEvent &change) -{ - if (!mObject) - return; - - switch (change.type) { - case ChangeEvent::ObjectsChanged: { - auto &objectsChange = static_cast(change); - - if (objectsChange.properties & ObjectsChangeEvent::ClassProperty) { - if (objectsChange.objects.contains(mObject)) { - updateProperties(); - updateCustomProperties(); - } else if (mObject->typeId() == Object::MapObjectType) { - auto mapObject = static_cast(mObject); - if (auto tile = mapObject->cell().tile()) { - if (mapObject->className().isEmpty() && objectsChange.objects.contains(tile)) { - updateProperties(); - updateCustomProperties(); - } - } - } - } - - break; - } - case ChangeEvent::LayerChanged: - case ChangeEvent::TileLayerChanged: - case ChangeEvent::ImageLayerChanged: - if (mObject == static_cast(change).layer) - updateProperties(); - break; - case ChangeEvent::MapObjectsChanged: - mapObjectsChanged(static_cast(change)); - break; - case ChangeEvent::ObjectGroupChanged: - if (mObject == static_cast(change).objectGroup) - updateProperties(); - break; - case ChangeEvent::TilesetChanged: - if (mObject == static_cast(change).tileset) - updateProperties(); - break; - case ChangeEvent::WangSetChanged: - if (mObject == static_cast(change).wangSet) - updateProperties(); - break; - default: - break; - } -} - -void PropertyBrowser::mapChanged() -{ - if (mObject == mMapDocument->map()) - updateProperties(); -} - -void PropertyBrowser::mapObjectsChanged(const MapObjectsChangeEvent &mapObjectsChange) -{ - if (!mObject || mObject->typeId() != Object::MapObjectType) - return; - if (!mapObjectsChange.mapObjects.contains(static_cast(mObject))) - return; - - updateProperties(); - - if (mapObjectsChange.properties & MapObject::CustomProperties) - updateCustomProperties(); -} - -void PropertyBrowser::tilesetChanged(Tileset *tileset) -{ - if (mObject == tileset) { - updateProperties(); - updateCustomProperties(); // Tileset may have been swapped - } -} - -void PropertyBrowser::tileChanged(Tile *tile) -{ - if (mObject == tile) - updateProperties(); -} - -void PropertyBrowser::tileTypeChanged(Tile *tile) -{ - if (mObject == tile) { - updateProperties(); - updateCustomProperties(); - } else if (mObject && mObject->typeId() == Object::MapObjectType) { - auto mapObject = static_cast(mObject); - if (mapObject->cell().tile() == tile && mapObject->className().isEmpty()) - updateProperties(); - } -} - -void PropertyBrowser::wangSetChanged(WangSet *wangSet) -{ - if (mObject == wangSet) - updateProperties(); -} - -static bool isAutomappingRulesMap(const MapDocument *mapDocument) -{ - if (!mapDocument) - return false; - - bool hasInputLayer = false; - bool hasOutputLayer = false; - - for (const Layer *layer : mapDocument->map()->allLayers()) { - if (layer->name().startsWith(QLatin1String("input"), Qt::CaseInsensitive)) - hasInputLayer |= layer->isTileLayer(); - else if (layer->name().startsWith(QLatin1String("output"), Qt::CaseInsensitive)) - hasOutputLayer = true; - } - - return hasInputLayer && hasOutputLayer; -} - -static void addAutomappingProperties(Properties &properties, const Object *object) -{ - auto addRuleOptions = [&] { - mergeProperties(properties, QVariantMap { - { QStringLiteral("Probability"), 1.0 }, - { QStringLiteral("ModX"), 1 }, - { QStringLiteral("ModY"), 1 }, - { QStringLiteral("OffsetX"), 0 }, - { QStringLiteral("OffsetY"), 0 }, - { QStringLiteral("NoOverlappingOutput"), false }, - { QStringLiteral("Disabled"), false }, - { QStringLiteral("IgnoreLock"), false }, - }); - }; - - switch (object->typeId()) { - case Object::LayerType: { - auto layer = static_cast(object); - - if (layer->name().startsWith(QLatin1String("input"), Qt::CaseInsensitive)) { - mergeProperties(properties, QVariantMap { - { QStringLiteral("AutoEmpty"), false }, - { QStringLiteral("IgnoreHorizontalFlip"), false }, - { QStringLiteral("IgnoreVerticalFlip"), false }, - { QStringLiteral("IgnoreDiagonalFlip"), false }, - // { QStringLiteral("IgnoreHexRotate120"), false }, - }); - } else if (layer->name().startsWith(QLatin1String("output"), Qt::CaseInsensitive)) { - mergeProperties(properties, QVariantMap { - { QStringLiteral("Probability"), 1.0 }, - }); - } - break; - } - case Object::MapType: - mergeProperties(properties, QVariantMap { - { QStringLiteral("DeleteTiles"), false }, - { QStringLiteral("MatchOutsideMap"), false }, - { QStringLiteral("OverflowBorder"), false }, - { QStringLiteral("WrapBorder"), false }, - { QStringLiteral("AutomappingRadius"), 0 }, - { QStringLiteral("NoOverlappingOutput"), false }, - { QStringLiteral("MatchInOrder"), false }, - }); - addRuleOptions(); - break; - case Object::MapObjectType: { - if (auto objectGroup = static_cast(object)->objectGroup()) - if (objectGroup->name().compare(QLatin1String("rule_options"), Qt::CaseInsensitive) == 0) - addRuleOptions(); - break; - } - default: - break; - } -} - -static bool checkAutomappingProperty(const Object *object, - const QString &name, - QVariant &value) -{ - Properties properties; - addAutomappingProperties(properties, object); - value = properties.value(name); - return value.isValid(); -} - -static bool anyObjectHasProperty(const QList &objects, const QString &name) -{ - for (Object *obj : objects) { - if (obj->hasProperty(name)) - return true; - } - return false; -} - -static bool propertyValueAffected(Object *currentObject, - Object *changedObject, - const QString &propertyName) -{ - if (currentObject == changedObject) - return true; - - // Changed property may be inherited - if (currentObject && currentObject->typeId() == Object::MapObjectType && changedObject->typeId() == Object::TileType) { - auto tile = static_cast(currentObject)->cell().tile(); - if (tile == changedObject && !currentObject->hasProperty(propertyName)) - return true; - } - - return false; -} - -static bool objectPropertiesRelevant(Document *document, Object *object) -{ - auto currentObject = document->currentObject(); - if (!currentObject) - return false; - - if (currentObject == object) - return true; - - if (currentObject->typeId() == Object::MapObjectType) - if (static_cast(currentObject)->cell().tile() == object) - return true; - - if (document->currentObjects().contains(object)) - return true; - - return false; -} - -static QStringList classNamesFor(const Object &object) -{ - QStringList names; - for (const auto type : Object::propertyTypes()) - if (type->isClass()) - if (static_cast(type)->isClassFor(object)) - names.append(type->name); - return names; -} - -void PropertyBrowser::propertyAdded(Object *object, const QString &name) -{ - if (!objectPropertiesRelevant(mDocument, object)) - return; - if (QtVariantProperty *property = mCustomPropertiesHelper.property(name)) { - if (propertyValueAffected(mObject, object, name)) - setCustomPropertyValue(property, object->property(name)); - } else { - const QVariant value = mObject->resolvedProperty(name); - addCustomProperty(name, value); - } - updateCustomPropertyColor(name); -} - -void PropertyBrowser::propertyRemoved(Object *object, const QString &name) -{ - auto property = mCustomPropertiesHelper.property(name); - if (!property) - return; - if (!objectPropertiesRelevant(mDocument, object)) - return; - - QVariant resolvedValue = mObject->resolvedProperty(name); - - if (!resolvedValue.isValid() && - !(isAutomappingRulesMap(mMapDocument) && checkAutomappingProperty(object, name, resolvedValue)) && - !anyObjectHasProperty(mDocument->currentObjects(), name)) { - // It's not a predefined property and no selected object has this - // property, so delete it. - - // First move up or down the currently selected item - QtBrowserItem *item = currentItem(); - if (item && item->property() == property) { - const QList siblings = item->parent()->children(); - if (siblings.count() > 1) { - int currentItemIndex = siblings.indexOf(item); - if (item == siblings.last()) { - setCurrentItem(siblings.at(currentItemIndex - 1)); - } else { - setCurrentItem(siblings.at(currentItemIndex + 1)); - } - } - } - - mCustomPropertiesHelper.deleteProperty(property); - return; - } - - if (propertyValueAffected(mObject, object, name)) { - // Property deleted from the current object, so reset the value. - setCustomPropertyValue(property, resolvedValue); - } - - updateCustomPropertyColor(name); -} - -void PropertyBrowser::propertyChanged(Object *object, const QString &name) -{ - auto property = mCustomPropertiesHelper.property(name); - if (!property) - return; - - if (propertyValueAffected(mObject, object, name)) - setCustomPropertyValue(property, object->property(name)); - - if (mDocument->currentObjects().contains(object)) - updateCustomPropertyColor(name); -} - -void PropertyBrowser::propertiesChanged(Object *object) -{ - if (objectPropertiesRelevant(mDocument, object)) - updateCustomProperties(); -} - -void PropertyBrowser::selectedObjectsChanged() -{ - updateCustomProperties(); -} - -void PropertyBrowser::selectedLayersChanged() -{ - updateCustomProperties(); -} - -void PropertyBrowser::selectedTilesChanged() -{ - updateCustomProperties(); -} - -void PropertyBrowser::propertyTypesChanged() -{ - if (!mObject) - return; - - if (auto classProperty = mIdToProperty.value(ClassProperty)) { - classProperty->setAttribute(QStringLiteral("suggestions"), - classNamesFor(*mObject)); - } - - // Don't do anything if there can't be any properties based on the class - if (mObject->typeId() == Object::MapObjectType) { - if (static_cast(mObject)->effectiveClassName().isEmpty()) - return; - } else if (mObject->className().isEmpty()) { - return; - } - - updateCustomProperties(); -} - -void PropertyBrowser::valueChanged(QtProperty *property, const QVariant &val) -{ - if (mUpdating) - return; - if (!mObject || !mDocument) - return; - if (!mPropertyToId.contains(property)) - return; - - const PropertyId id = mPropertyToId.value(property); - - if (id == ClassProperty) { - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new ChangeClassName(mDocument, - mDocument->currentObjects(), - val.toString())); - return; - } - - switch (mObject->typeId()) { - case Object::MapType: applyMapValue(id, val); break; - case Object::MapObjectType: applyMapObjectValue(id, val); break; - case Object::LayerType: applyLayerValue(id, val); break; - case Object::TilesetType: applyTilesetValue(id, val); break; - case Object::TileType: applyTileValue(id, val); break; - case Object::WangSetType: applyWangSetValue(id, val); break; - case Object::WangColorType: applyWangColorValue(id, val); break; - case Object::ProjectType: break; - case Object::WorldType: break; - } -} - -void PropertyBrowser::customPropertyValueChanged(const QStringList &path, const QVariant &value) -{ - if (mUpdating) - return; - if (!mObject || !mDocument) - return; - - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new SetProperty(mDocument, - mDocument->currentObjects(), - path, value)); -} - -void PropertyBrowser::resetProperty(QtProperty *property) -{ - auto typeId = mVariantManager->propertyType(property); - if (typeId == QMetaType::QColor) - mVariantManager->setValue(property, QColor()); - else - qWarning() << "Resetting of property type not supported right now"; -} - -void PropertyBrowser::addMapProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Map")); - - addClassProperty(groupProperty); - - QtVariantProperty *orientationProperty = - addProperty(OrientationProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Orientation"), - groupProperty); - - orientationProperty->setAttribute(QLatin1String("enumNames"), mOrientationNames); - - addProperty(WidthProperty, QMetaType::Int, tr("Width"), groupProperty)->setEnabled(false); - addProperty(HeightProperty, QMetaType::Int, tr("Height"), groupProperty)->setEnabled(false); - auto tileWidthProperty = addProperty(TileWidthProperty, QMetaType::Int, tr("Tile Width"), groupProperty); - auto tileHeightProperty = addProperty(TileHeightProperty, QMetaType::Int, tr("Tile Height"), groupProperty); - addProperty(InfiniteProperty, QMetaType::Bool, tr("Infinite"), groupProperty); - - tileWidthProperty->setAttribute(QStringLiteral("minimum"), 1); - tileHeightProperty->setAttribute(QStringLiteral("minimum"), 1); - - addProperty(HexSideLengthProperty, QMetaType::Int, tr("Tile Side Length (Hex)"), groupProperty); - - QtVariantProperty *staggerAxisProperty = - addProperty(StaggerAxisProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Stagger Axis"), - groupProperty); - - staggerAxisProperty->setAttribute(QLatin1String("enumNames"), mStaggerAxisNames); - - QtVariantProperty *staggerIndexProperty = - addProperty(StaggerIndexProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Stagger Index"), - groupProperty); - - staggerIndexProperty->setAttribute(QLatin1String("enumNames"), mStaggerIndexNames); - - addProperty(ParallaxOriginProperty, QMetaType::QPointF, tr("Parallax Origin"), groupProperty); - - QtVariantProperty *layerFormatProperty = - addProperty(LayerFormatProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Tile Layer Format"), - groupProperty); - - layerFormatProperty->setAttribute(QLatin1String("enumNames"), mLayerFormatNames); - - QtVariantProperty *chunkWidthProperty = addProperty(ChunkWidthProperty, QMetaType::Int, tr("Output Chunk Width"), groupProperty); - QtVariantProperty *chunkHeightProperty = addProperty(ChunkHeightProperty, QMetaType::Int, tr("Output Chunk Height"), groupProperty); - - chunkWidthProperty->setAttribute(QLatin1String("minimum"), CHUNK_SIZE_MIN); - chunkHeightProperty->setAttribute(QLatin1String("minimum"), CHUNK_SIZE_MIN); - - QtVariantProperty *renderOrderProperty = - addProperty(RenderOrderProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Tile Render Order"), - groupProperty); - - addProperty(CompressionLevelProperty, QMetaType::Int, tr("Compression Level"), groupProperty); - - renderOrderProperty->setAttribute(QLatin1String("enumNames"), mRenderOrderNames); - - addProperty(BackgroundColorProperty, QMetaType::QColor, tr("Background Color"), groupProperty); - addProperty(groupProperty); -} - -enum MapObjectFlags { - ObjectHasDimensions = 0x1, - ObjectHasTile = 0x2, - ObjectIsText = 0x4 -}; - -static int mapObjectFlags(const MapObject *mapObject) -{ - int flags = 0; - if (mapObject->hasDimensions()) - flags |= ObjectHasDimensions; - if (!mapObject->cell().isEmpty()) - flags |= ObjectHasTile; - if (mapObject->shape() == MapObject::Text) - flags |= ObjectIsText; - return flags; -} - -void PropertyBrowser::addMapObjectProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Object")); - - addProperty(IdProperty, QMetaType::Int, tr("ID"), groupProperty)->setEnabled(false); - addProperty(TemplateProperty, filePathTypeId(), tr("Template"), groupProperty)->setEnabled(false); - addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty); - - addClassProperty(groupProperty); - - if (mMapDocument->allowHidingObjects()) - addProperty(VisibleProperty, QMetaType::Bool, tr("Visible"), groupProperty); - - addProperty(XProperty, QMetaType::Double, tr("X"), groupProperty); - addProperty(YProperty, QMetaType::Double, tr("Y"), groupProperty); - - auto mapObject = static_cast(mObject); - mMapObjectFlags = mapObjectFlags(mapObject); - - if (mMapObjectFlags & ObjectHasDimensions) { - addProperty(WidthProperty, QMetaType::Double, tr("Width"), groupProperty); - addProperty(HeightProperty, QMetaType::Double, tr("Height"), groupProperty); - } - - bool isPoint = mapObject->shape() == MapObject::Point; - addProperty(RotationProperty, QMetaType::Double, tr("Rotation"), groupProperty)->setEnabled(!isPoint); - - if (mMapObjectFlags & ObjectHasTile) { - QtVariantProperty *flippingProperty = - addProperty(FlippingProperty, VariantPropertyManager::flagTypeId(), - tr("Flipping"), groupProperty); - - flippingProperty->setAttribute(QLatin1String("flagNames"), mFlippingFlagNames); - } - - if (mMapObjectFlags & ObjectIsText) { - addProperty(TextProperty, QMetaType::QString, tr("Text"), groupProperty)->setAttribute(QLatin1String("multiline"), true); - addProperty(TextAlignmentProperty, VariantPropertyManager::alignmentTypeId(), tr("Alignment"), groupProperty); - addProperty(FontProperty, QMetaType::QFont, tr("Font"), groupProperty); - addProperty(WordWrapProperty, QMetaType::Bool, tr("Word Wrap"), groupProperty); - addProperty(ColorProperty, QMetaType::QColor, tr("Color"), groupProperty); - } - - addProperty(groupProperty); -} - -void PropertyBrowser::addLayerProperties(QtProperty *parent) -{ - addProperty(IdProperty, QMetaType::Int, tr("ID"), parent)->setEnabled(false); - addProperty(NameProperty, QMetaType::QString, tr("Name"), parent); - addClassProperty(parent); - addProperty(VisibleProperty, QMetaType::Bool, tr("Visible"), parent); - addProperty(LockedProperty, QMetaType::Bool, tr("Locked"), parent); - - QtVariantProperty *opacityProperty = - addProperty(OpacityProperty, QMetaType::Double, tr("Opacity"), parent); - opacityProperty->setAttribute(QLatin1String("minimum"), 0.0); - opacityProperty->setAttribute(QLatin1String("maximum"), 1.0); - opacityProperty->setAttribute(QLatin1String("singleStep"), 0.1); - addProperty(TintColorProperty, QMetaType::QColor, tr("Tint Color"), parent); - - addProperty(OffsetXProperty, QMetaType::Double, tr("Horizontal Offset"), parent); - addProperty(OffsetYProperty, QMetaType::Double, tr("Vertical Offset"), parent); - - QtVariantProperty *parallaxProperty = - addProperty(ParallaxFactorProperty, QMetaType::QPointF, tr("Parallax Factor"), parent); - parallaxProperty->setAttribute(QLatin1String("singleStep"), 0.1); -} - -void PropertyBrowser::addTileLayerProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Tile Layer")); - addLayerProperties(groupProperty); - addProperty(groupProperty); -} - -void PropertyBrowser::addObjectGroupProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Object Layer")); - addLayerProperties(groupProperty); - - addProperty(ColorProperty, QMetaType::QColor, tr("Color"), groupProperty); - - QtVariantProperty *drawOrderProperty = - addProperty(DrawOrderProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Drawing Order"), - groupProperty); - - drawOrderProperty->setAttribute(QLatin1String("enumNames"), mDrawOrderNames); - - addProperty(groupProperty); -} - -void PropertyBrowser::addImageLayerProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Image Layer")); - addLayerProperties(groupProperty); - - QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty, - filePathTypeId(), - tr("Image"), groupProperty); - - imageSourceProperty->setAttribute(QLatin1String("filter"), - Utils::readableImageFormatsFilter()); - - addProperty(ColorProperty, QMetaType::QColor, tr("Transparent Color"), groupProperty); - - addProperty(RepeatXProperty, QMetaType::Bool, tr("Repeat X"), groupProperty); - addProperty(RepeatYProperty, QMetaType::Bool, tr("Repeat Y"), groupProperty); - - addProperty(groupProperty); -} - -void PropertyBrowser::addGroupLayerProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Group Layer")); - addLayerProperties(groupProperty); - addProperty(groupProperty); -} - -void PropertyBrowser::addTilesetProperties() -{ - const Tileset *tileset = static_cast(mObject); - - QtProperty *groupProperty = mGroupManager->addProperty(tr("Tileset")); - - if (mMapDocument) { - auto property = addProperty(FileNameProperty, filePathTypeId(), tr("Filename"), groupProperty); - - QString filter = QCoreApplication::translate("MainWindow", "All Files (*)"); - FormatHelper helper(FileFormat::Read, filter); - - property->setAttribute(QStringLiteral("filter"), helper.filter()); - } - - QtVariantProperty *nameProperty = addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty); - nameProperty->setEnabled(mTilesetDocument); - - addClassProperty(groupProperty); - - QtVariantProperty *alignmentProperty = - addProperty(ObjectAlignmentProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Object Alignment"), - groupProperty); - alignmentProperty->setAttribute(QLatin1String("enumNames"), mAlignmentNames); - alignmentProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *tileOffsetProperty = addProperty(TileOffsetProperty, QMetaType::QPoint, tr("Drawing Offset"), groupProperty); - tileOffsetProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *tileRenderSizeProperty = - addProperty(TileRenderSizeProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Tile Render Size"), - groupProperty); - tileRenderSizeProperty->setAttribute(QLatin1String("enumNames"), mTileRenderSizeNames); - tileRenderSizeProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *fillModeProperty = - addProperty(FillModeProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Fill Mode"), - groupProperty); - fillModeProperty->setAttribute(QLatin1String("enumNames"), mFillModeNames); - fillModeProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *backgroundProperty = addProperty(BackgroundColorProperty, QMetaType::QColor, tr("Background Color"), groupProperty); - backgroundProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *orientationProperty = - addProperty(OrientationProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Orientation"), - groupProperty); - orientationProperty->setAttribute(QLatin1String("enumNames"), mTilesetOrientationNames); - - QtVariantProperty *gridWidthProperty = addProperty(GridWidthProperty, QMetaType::Int, tr("Grid Width"), groupProperty); - gridWidthProperty->setEnabled(mTilesetDocument); - gridWidthProperty->setAttribute(QLatin1String("minimum"), 1); - QtVariantProperty *gridHeightProperty = addProperty(GridHeightProperty, QMetaType::Int, tr("Grid Height"), groupProperty); - gridHeightProperty->setEnabled(mTilesetDocument); - gridHeightProperty->setAttribute(QLatin1String("minimum"), 1); - - QtVariantProperty *columnsProperty = addProperty(ColumnCountProperty, QMetaType::Int, tr("Columns"), groupProperty); - columnsProperty->setAttribute(QLatin1String("minimum"), 1); - - QtVariantProperty *transformationsGroupProperty = mVariantManager->addProperty(VariantPropertyManager::unstyledGroupTypeId(), tr("Allowed Transformations")); - - QtVariantProperty *flipHorizontallyProperty = addProperty(AllowFlipHorizontallyProperty, QMetaType::Bool, tr("Flip Horizontally"), transformationsGroupProperty); - QtVariantProperty *flipVerticallyProperty = addProperty(AllowFlipVerticallyProperty, QMetaType::Bool, tr("Flip Vertically"), transformationsGroupProperty); - QtVariantProperty *rotateProperty = addProperty(AllowRotateProperty, QMetaType::Bool, tr("Rotate"), transformationsGroupProperty); - QtVariantProperty *randomProperty = addProperty(PreferUntransformedProperty, QMetaType::Bool, tr("Prefer Untransformed Tiles"), transformationsGroupProperty); - flipHorizontallyProperty->setEnabled(mTilesetDocument); - flipVerticallyProperty->setEnabled(mTilesetDocument); - rotateProperty->setEnabled(mTilesetDocument); - randomProperty->setEnabled(mTilesetDocument); - - groupProperty->addSubProperty(transformationsGroupProperty); - - // Next properties we should add only for non 'Collection of Images' tilesets - if (!tileset->isCollection()) { - QtVariantProperty *parametersProperty = - addProperty(TilesetImageParametersProperty, VariantPropertyManager::tilesetParametersTypeId(), tr("Image"), groupProperty); - - parametersProperty->setEnabled(mTilesetDocument); - - QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty, QMetaType::QString, tr("Source"), parametersProperty); - QtVariantProperty *tileWidthProperty = addProperty(TileWidthProperty, QMetaType::Int, tr("Tile Width"), parametersProperty); - QtVariantProperty *tileHeightProperty = addProperty(TileHeightProperty, QMetaType::Int, tr("Tile Height"), parametersProperty); - QtVariantProperty *marginProperty = addProperty(MarginProperty, QMetaType::Int, tr("Margin"), parametersProperty); - QtVariantProperty *spacingProperty = addProperty(SpacingProperty, QMetaType::Int, tr("Spacing"), parametersProperty); - QtVariantProperty *colorProperty = addProperty(ColorProperty, QMetaType::QColor, tr("Transparent Color"), parametersProperty); - - // These properties can't be directly edited. To change the parameters, - // the TilesetParametersEdit is used. - imageSourceProperty->setEnabled(false); - tileWidthProperty->setEnabled(false); - tileHeightProperty->setEnabled(false); - marginProperty->setEnabled(false); - spacingProperty->setEnabled(false); - colorProperty->setEnabled(false); - } - addProperty(groupProperty); -} - -void PropertyBrowser::addTileProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Tile")); - addProperty(IdProperty, QMetaType::Int, tr("ID"), groupProperty)->setEnabled(false); - - addClassProperty(groupProperty)->setEnabled(mTilesetDocument); - - addProperty(WidthProperty, QMetaType::Int, tr("Width"), groupProperty)->setEnabled(false); - addProperty(HeightProperty, QMetaType::Int, tr("Height"), groupProperty)->setEnabled(false); - - QtVariantProperty *probabilityProperty = addProperty(TileProbabilityProperty, - QMetaType::Double, - tr("Probability"), - groupProperty); - probabilityProperty->setAttribute(QLatin1String("decimals"), 3); - probabilityProperty->setToolTip(tr("Relative chance this tile will be picked")); - probabilityProperty->setEnabled(mTilesetDocument); - - const Tile *tile = static_cast(mObject); - if (!tile->imageSource().isEmpty()) { - QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty, - filePathTypeId(), - tr("Image"), groupProperty); - imageSourceProperty->setAttribute(QLatin1String("filter"), - Utils::readableImageFormatsFilter()); - imageSourceProperty->setEnabled(mTilesetDocument); - } - - QtVariantProperty *imageRectProperty = addProperty(ImageRectProperty, - QMetaType::QRect, - tr("Image Rect"), groupProperty); - imageRectProperty->setEnabled(mTilesetDocument && tile->tileset()->isCollection()); - imageRectProperty->setAttribute(QLatin1String("constraint"), tile->image().rect()); - - addProperty(groupProperty); -} - -void PropertyBrowser::addWangSetProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Terrain Set")); - QtVariantProperty *nameProperty = addProperty(NameProperty, QMetaType::QString, tr("Name"), groupProperty); - QtVariantProperty *classProperty = addClassProperty(groupProperty); - QtVariantProperty *typeProperty = addProperty(WangSetTypeProperty, - QtVariantPropertyManager::enumTypeId(), - tr("Type"), - groupProperty); - QtVariantProperty *colorCountProperty = addProperty(ColorCountProperty, QMetaType::Int, tr("Terrain Count"), groupProperty); - - typeProperty->setAttribute(QLatin1String("enumNames"), mWangSetTypeNames); - typeProperty->setAttribute(QLatin1String("enumIcons"), QVariant::fromValue(mWangSetIcons)); - - colorCountProperty->setAttribute(QLatin1String("minimum"), 0); - colorCountProperty->setAttribute(QLatin1String("maximum"), WangId::MAX_COLOR_COUNT); - - nameProperty->setEnabled(mTilesetDocument); - classProperty->setEnabled(mTilesetDocument); - typeProperty->setEnabled(mTilesetDocument); - colorCountProperty->setEnabled(mTilesetDocument); - - addProperty(groupProperty); -} - -void PropertyBrowser::addWangColorProperties() -{ - QtProperty *groupProperty = mGroupManager->addProperty(tr("Terrain")); - QtVariantProperty *nameProperty = addProperty(NameProperty, - QMetaType::QString, - tr("Name"), - groupProperty); - QtVariantProperty *classProperty = addClassProperty(groupProperty); - QtVariantProperty *colorProperty = addProperty(ColorProperty, - QMetaType::QColor, - tr("Color"), - groupProperty); - QtVariantProperty *probabilityProperty = addProperty(WangColorProbabilityProperty, - QMetaType::Double, - tr("Probability"), - groupProperty); - - probabilityProperty->setAttribute(QLatin1String("minimum"), 0.01); - - nameProperty->setEnabled(mTilesetDocument); - classProperty->setEnabled(mTilesetDocument); - colorProperty->setEnabled(mTilesetDocument); - probabilityProperty->setEnabled(mTilesetDocument); - - addProperty(groupProperty); -} - -QtVariantProperty *PropertyBrowser::addClassProperty(QtProperty *parent) -{ - QtVariantProperty *classProperty = addProperty(ClassProperty, - QMetaType::QString, - tr("Class"), - parent); - - classProperty->setAttribute(QLatin1String("suggestions"), - classNamesFor(*mObject)); - - return classProperty; -} - -void PropertyBrowser::applyMapValue(PropertyId id, const QVariant &val) -{ - QUndoCommand *command = nullptr; - - switch (id) { - case TileWidthProperty: - command = new ChangeMapTileSize(mMapDocument, QSize(val.toInt(), - mMapDocument->map()->tileHeight())); - break; - case TileHeightProperty: - command = new ChangeMapTileSize(mMapDocument, QSize(mMapDocument->map()->tileWidth(), - val.toInt())); - break; - case InfiniteProperty: { - const bool infinite = val.toInt(); - auto changePropertyCommand = new ChangeMapInfinite(mMapDocument, infinite); - - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->beginMacro(changePropertyCommand->text()); - - if (!infinite) { - QRect mapBounds(QPoint(0, 0), mMapDocument->map()->size()); - - LayerIterator iterator(mMapDocument->map()); - while (Layer *layer = iterator.next()) { - if (TileLayer *tileLayer = dynamic_cast(layer)) - mapBounds = mapBounds.united(tileLayer->region().boundingRect()); - } - - if (mapBounds.size() == QSize(0, 0)) - mapBounds.setSize(QSize(1, 1)); - - mMapDocument->resizeMap(mapBounds.size(), -mapBounds.topLeft(), false); - } - - undoStack->push(changePropertyCommand); - undoStack->endMacro(); - break; - } - case OrientationProperty: { - Map::Orientation orientation = static_cast(val.toInt() + 1); - command = new ChangeMapOrientation(mMapDocument, orientation); - break; - } - case HexSideLengthProperty: { - command = new ChangeMapHexSideLength(mMapDocument, val.toInt()); - break; - } - case StaggerAxisProperty: { - Map::StaggerAxis staggerAxis = static_cast(val.toInt()); - command = new ChangeMapStaggerAxis(mMapDocument, staggerAxis); - break; - } - case StaggerIndexProperty: { - Map::StaggerIndex staggerIndex = static_cast(val.toInt()); - command = new ChangeMapStaggerIndex(mMapDocument, staggerIndex); - break; - } - case ParallaxOriginProperty: { - command = new ChangeMapParallaxOrigin(mMapDocument, val.value()); - break; - } - case LayerFormatProperty: { - Map::LayerDataFormat format = mLayerFormatValues.at(val.toInt()); - command = new ChangeMapLayerDataFormat(mMapDocument, format); - break; - } - case RenderOrderProperty: { - Map::RenderOrder renderOrder = static_cast(val.toInt()); - command = new ChangeMapRenderOrder(mMapDocument, renderOrder); - break; - } - case BackgroundColorProperty: - command = new ChangeMapBackgroundColor(mMapDocument, val.value()); - break; - case CompressionLevelProperty: - command = new ChangeMapCompressionLevel(mMapDocument, val.toInt()); - break; - case ChunkWidthProperty: { - QSize chunkSize = mMapDocument->map()->chunkSize(); - chunkSize.setWidth(val.toInt()); - command = new ChangeMapChunkSize(mMapDocument, chunkSize); - break; - } - case ChunkHeightProperty: { - QSize chunkSize = mMapDocument->map()->chunkSize(); - chunkSize.setHeight(val.toInt()); - command = new ChangeMapChunkSize(mMapDocument, chunkSize); - break; - } - default: - break; - } - - if (command) - mDocument->undoStack()->push(command); -} - -QUndoCommand *PropertyBrowser::applyMapObjectValueTo(PropertyId id, const QVariant &val, MapObject *mapObject) -{ - QUndoCommand *command = nullptr; - - switch (id) { - default: { - MapObject::Property property; - - switch (id) { - case NameProperty: property = MapObject::NameProperty; break; - case VisibleProperty: property = MapObject::VisibleProperty; break; - case TextProperty: property = MapObject::TextProperty; break; - case FontProperty: property = MapObject::TextFontProperty; break; - case TextAlignmentProperty: property = MapObject::TextAlignmentProperty; break; - case WordWrapProperty: property = MapObject::TextWordWrapProperty; break; - case ColorProperty: property = MapObject::TextColorProperty; break; - default: - return nullptr; // unrecognized property - } - - command = new ChangeMapObject(mDocument, mapObject, property, val); - break; - } - case XProperty: { - command = new ChangeMapObject(mDocument, mapObject, - MapObject::PositionProperty, - QPointF(val.toReal(), mapObject->y())); - break; - } - case YProperty: { - command = new ChangeMapObject(mDocument, mapObject, - MapObject::PositionProperty, - QPointF(mapObject->x(), val.toReal())); - break; - } - case WidthProperty: { - command = new ChangeMapObject(mDocument, mapObject, - MapObject::SizeProperty, - QSizeF(val.toReal(), mapObject->height())); - break; - } - case HeightProperty: { - command = new ChangeMapObject(mDocument, mapObject, - MapObject::SizeProperty, - QSizeF(mapObject->width(), val.toReal())); - break; - } - case RotationProperty: - if (mapObject->canRotate()) { - command = new ChangeMapObject(mDocument, mapObject, - MapObject::RotationProperty, - val.toDouble()); - } - break; - case FlippingProperty: { - const int flippingFlags = val.toInt(); - - MapObjectCell mapObjectCell; - mapObjectCell.object = mapObject; - mapObjectCell.cell = mapObject->cell(); - mapObjectCell.cell.setFlippedHorizontally(flippingFlags & 1); - mapObjectCell.cell.setFlippedVertically(flippingFlags & 2); - - command = new ChangeMapObjectCells(mDocument, { mapObjectCell }); - - command->setText(QCoreApplication::translate("Undo Commands", - "Flip %n Object(s)", - nullptr, - mMapDocument->selectedObjects().size())); - break; - } - } - - return command; -} - -void PropertyBrowser::applyMapObjectValue(PropertyId id, const QVariant &val) -{ - MapObject *mapObject = static_cast(mObject); - - QUndoCommand *command = applyMapObjectValueTo(id, val, mapObject); - if (!command) - return; - - if (mMapDocument->selectedObjects().size() == 1) { - mDocument->undoStack()->push(command); - return; - } - - mDocument->undoStack()->beginMacro(command->text()); - mDocument->undoStack()->push(command); - - for (MapObject *obj : mMapDocument->selectedObjects()) { - if (obj != mapObject) { - if (QUndoCommand *cmd = applyMapObjectValueTo(id, val, obj)) - mDocument->undoStack()->push(cmd); - } - } - - mDocument->undoStack()->endMacro(); -} - -template -QList layersOfType(const QList &layers, Layer::TypeFlag typeFlag) -{ - QList result; - for (Layer *layer : layers) - if (layer->layerType() == typeFlag) - result.append(static_cast(layer)); - return result; -} - -void PropertyBrowser::applyLayerValue(PropertyId id, const QVariant &val) -{ - const auto &layers = mMapDocument->selectedLayers(); - if (layers.isEmpty()) - return; - - QUndoCommand *command = nullptr; - - switch (id) { - case NameProperty: - command = new SetLayerName(mMapDocument, layers, val.toString()); - break; - case VisibleProperty: - command = new SetLayerVisible(mMapDocument, layers, val.toBool()); - break; - case LockedProperty: - command = new SetLayerLocked(mMapDocument, layers, val.toBool()); - break; - case OpacityProperty: - command = new SetLayerOpacity(mMapDocument, layers, val.toDouble()); - break; - case TintColorProperty: - command = new SetLayerTintColor(mMapDocument, layers, val.value()); - break; - case OffsetXProperty: - case OffsetYProperty: { - QVector offsets; - for (const Layer *layer : layers) - offsets.append(layer->offset()); - - if (id == OffsetXProperty) { - for (QPointF &offset : offsets) - offset.setX(val.toDouble()); - } else { - for (QPointF &offset : offsets) - offset.setY(val.toDouble()); - } - - command = new SetLayerOffset(mMapDocument, layers, offsets); - break; - } - case ParallaxFactorProperty: - command = new SetLayerParallaxFactor(mMapDocument, layers, val.toPointF()); - break; - default: { - Layer *currentLayer = static_cast(mObject); - switch (currentLayer->layerType()) { - case Layer::TileLayerType: - command = applyTileLayerValueTo(id, val, layersOfType(layers, Layer::TileLayerType)); - break; - case Layer::ObjectGroupType: - command = applyObjectGroupValueTo(id, val, layersOfType(layers, Layer::ObjectGroupType)); - break; - case Layer::ImageLayerType: - command = applyImageLayerValueTo(id, val, layersOfType(layers, Layer::ImageLayerType)); - break; - case Layer::GroupLayerType: - command = applyGroupLayerValueTo(id, val, layersOfType(layers, Layer::GroupLayerType)); - break; - } - break; - } - } - - if (command) - mDocument->undoStack()->push(command); -} - -QUndoCommand *PropertyBrowser::applyTileLayerValueTo(PropertyId id, const QVariant &val, QList tileLayers) -{ - Q_UNUSED(id) - Q_UNUSED(val) - Q_UNUSED(tileLayers) - - return nullptr; -} - -QUndoCommand *PropertyBrowser::applyObjectGroupValueTo(PropertyId id, const QVariant &val, QList objectGroups) -{ - if (objectGroups.isEmpty()) - return nullptr; - - switch (id) { - case ColorProperty: { - const QColor color = val.value(); - return new ChangeObjectGroupColor(mMapDocument, - std::move(objectGroups), - color); - } - case DrawOrderProperty: { - ObjectGroup::DrawOrder drawOrder = static_cast(val.toInt()); - return new ChangeObjectGroupDrawOrder(mMapDocument, - std::move(objectGroups), - drawOrder); - } - default: - return nullptr; - } -} - -QUndoCommand *PropertyBrowser::applyImageLayerValueTo(PropertyId id, const QVariant &val, QList imageLayers) -{ - if (imageLayers.isEmpty()) - return nullptr; - - switch (id) { - case ImageSourceProperty: - return new ChangeImageLayerImageSource(mMapDocument, std::move(imageLayers), - val.value().url); - case ColorProperty: - return new ChangeImageLayerTransparentColor(mMapDocument, std::move(imageLayers), - val.value()); - case RepeatXProperty: - return new ChangeImageLayerRepeatX(mMapDocument, std::move(imageLayers), val.toBool()); - case RepeatYProperty: - return new ChangeImageLayerRepeatY(mMapDocument, std::move(imageLayers), val.toBool()); - default: - return nullptr; - } -} - -QUndoCommand *PropertyBrowser::applyGroupLayerValueTo(PropertyId id, const QVariant &val, QList groupLayers) -{ - Q_UNUSED(id) - Q_UNUSED(val) - Q_UNUSED(groupLayers) - - return nullptr; -} - -void PropertyBrowser::applyTilesetValue(PropertyId id, const QVariant &val) -{ - Tileset *tileset = static_cast(mObject); - QUndoStack *undoStack = mDocument->undoStack(); - - switch (id) { - case FileNameProperty: { - FilePath filePath = val.value(); - QString error; - SharedTileset newTileset = TilesetManager::instance()->loadTileset(filePath.url.toLocalFile(), &error); - if (!newTileset) { - QMessageBox::critical(window(), tr("Error Reading Tileset"), error); - return; - } - - int index = mMapDocument->map()->tilesets().indexOf(tileset->sharedFromThis()); - if (index != -1) - undoStack->push(new ReplaceTileset(mMapDocument, index, newTileset)); - - break; - } - case NameProperty: - Q_ASSERT(mTilesetDocument); - undoStack->push(new RenameTileset(mTilesetDocument, val.toString())); - break; - case ObjectAlignmentProperty: { - Q_ASSERT(mTilesetDocument); - const auto objectAlignment = static_cast(val.toInt()); - undoStack->push(new ChangeTilesetObjectAlignment(mTilesetDocument, - objectAlignment)); - break; - } - case TileRenderSizeProperty: { - Q_ASSERT(mTilesetDocument); - const auto tileRenderSize = static_cast(val.toInt()); - undoStack->push(new ChangeTilesetTileRenderSize(mTilesetDocument, - tileRenderSize)); - break; - } - case FillModeProperty: { - Q_ASSERT(mTilesetDocument); - const auto fillMode = static_cast(val.toInt()); - undoStack->push(new ChangeTilesetFillMode(mTilesetDocument, - fillMode)); - break; - } - case TileOffsetProperty: - Q_ASSERT(mTilesetDocument); - undoStack->push(new ChangeTilesetTileOffset(mTilesetDocument, - val.toPoint())); - break; - case OrientationProperty: { - Q_ASSERT(mTilesetDocument); - auto orientation = static_cast(val.toInt()); - undoStack->push(new ChangeTilesetOrientation(mTilesetDocument, - orientation)); - break; - } - case GridWidthProperty: { - Q_ASSERT(mTilesetDocument); - QSize gridSize = tileset->gridSize(); - gridSize.setWidth(val.toInt()); - undoStack->push(new ChangeTilesetGridSize(mTilesetDocument, - gridSize)); - break; - } - case GridHeightProperty: { - Q_ASSERT(mTilesetDocument); - QSize gridSize = tileset->gridSize(); - gridSize.setHeight(val.toInt()); - undoStack->push(new ChangeTilesetGridSize(mTilesetDocument, - gridSize)); - break; - } - case ColumnCountProperty: - Q_ASSERT(mTilesetDocument); - undoStack->push(new ChangeTilesetColumnCount(mTilesetDocument, - val.toInt())); - break; - case BackgroundColorProperty: - Q_ASSERT(mTilesetDocument); - undoStack->push(new ChangeTilesetBackgroundColor(mTilesetDocument, - val.value())); - break; - case AllowFlipHorizontallyProperty: - case AllowFlipVerticallyProperty: - case AllowRotateProperty: - case PreferUntransformedProperty: { - Q_ASSERT(mTilesetDocument); - - Tileset::TransformationFlag flag = Tileset::NoTransformation; - switch (id) { - case AllowFlipHorizontallyProperty: - flag = Tileset::AllowFlipHorizontally; - break; - case AllowFlipVerticallyProperty: - flag = Tileset::AllowFlipVertically; - break; - case AllowRotateProperty: - flag = Tileset::AllowRotate; - break; - case PreferUntransformedProperty: - flag = Tileset::PreferUntransformed; - break; - default: - return; - } - - auto flags = tileset->transformationFlags(); - flags.setFlag(flag, val.toBool()); - - undoStack->push(new ChangeTilesetTransformationFlags(mTilesetDocument, flags)); - break; - } - default: - break; - } -} - -void PropertyBrowser::applyTileValue(PropertyId id, const QVariant &val) -{ - Q_ASSERT(mTilesetDocument); - - Tile *tile = static_cast(mObject); - QUndoStack *undoStack = mDocument->undoStack(); - - switch (id) { - case TileProbabilityProperty: - undoStack->push(new ChangeTileProbability(mTilesetDocument, - mTilesetDocument->selectedTiles(), - val.toFloat())); - break; - case ImageRectProperty: - undoStack->push(new ChangeTileImageRect(mTilesetDocument, - { tile }, { val.toRect() })); - break; - case ImageSourceProperty: { - const FilePath filePath = val.value(); - undoStack->push(new ChangeTileImageSource(mTilesetDocument, - tile, filePath.url)); - break; - } - default: - break; - } -} - -void PropertyBrowser::applyWangSetValue(PropertyId id, const QVariant &val) -{ - Q_ASSERT(mTilesetDocument); - - WangSet *wangSet = static_cast(mObject); - - switch (id) { - case NameProperty: - mDocument->undoStack()->push(new RenameWangSet(mTilesetDocument, - wangSet, - val.toString())); - break; - case WangSetTypeProperty: { - auto type = static_cast(val.toInt()); - mDocument->undoStack()->push(new ChangeWangSetType(mTilesetDocument, - wangSet, - type)); - break; - } - case ColorCountProperty: - mDocument->undoStack()->push(new ChangeWangSetColorCount(mTilesetDocument, - wangSet, - val.toInt())); - break; - default: - break; - } -} - -void PropertyBrowser::applyWangColorValue(PropertyId id, const QVariant &val) -{ - Q_ASSERT(mTilesetDocument); - - WangColor *wangColor = static_cast(mObject); - - switch (id) { - case NameProperty: - mDocument->undoStack()->push(new ChangeWangColorName(mTilesetDocument, - wangColor, - val.toString())); - break; - case ColorProperty: - mDocument->undoStack()->push(new ChangeWangColorColor(mTilesetDocument, - wangColor, - val.value())); - break; - case WangColorProbabilityProperty: - mDocument->undoStack()->push(new ChangeWangColorProbability(mTilesetDocument, - wangColor, - val.toDouble())); - break; - default: - break; - } -} - -/** - * @warning This function does not add the property to the view. - */ -QtVariantProperty *PropertyBrowser::createProperty(PropertyId id, int type, - const QString &name) -{ - Q_ASSERT(!mIdToProperty.contains(id)); - - QtVariantProperty *property = mVariantManager->addProperty(type, name); - if (!property) { - // fall back to string property for unsupported property types - property = mVariantManager->addProperty(QMetaType::QString, name); - } - - if (type == QMetaType::Bool) - property->setAttribute(QLatin1String("textVisible"), false); - - mPropertyToId.insert(property, id); - mIdToProperty.insert(id, property); - - return property; -} - -QtVariantProperty *PropertyBrowser::createCustomProperty(const QString &name, - const QVariant &value) -{ - Q_ASSERT(mObject); - - QtVariantProperty *property = mCustomPropertiesHelper.createProperty(name, value); - - if (mObject->isPartOfTileset()) - property->setEnabled(mTilesetDocument); - - return property; -} - -QtVariantProperty *PropertyBrowser::addProperty(PropertyId id, int type, - const QString &name, - QtProperty *parent) -{ - QtVariantProperty *property = createProperty(id, type, name); - parent->addSubProperty(property); - return property; -} - -QtVariantProperty *PropertyBrowser::addCustomProperty(const QString &name, const QVariant &value) -{ - // Determine the property preceding the new property, if any - const QList properties = mCustomPropertiesGroup->subProperties(); - QtProperty *precedingProperty = nullptr; - for (int i = 0; i < properties.size(); ++i) { - if (properties.at(i)->propertyName() < name) - precedingProperty = properties.at(i); - else - break; - } - - QScopedValueRollback updating(mUpdating, true); - QtVariantProperty *property = createCustomProperty(name, value); - mCustomPropertiesGroup->insertSubProperty(property, precedingProperty); - - // Collapse custom color properties, to save space - if (value.userType() == QMetaType::QColor) - setExpanded(items(property).constFirst(), false); - - return property; -} - -void PropertyBrowser::setCustomPropertyValue(QtVariantProperty *property, - const QVariant &value) -{ - const QVariant displayValue = toDisplayValue(value); - - if (displayValue.userType() != property->valueType()) { - // Re-creating the property is necessary to change its type - recreateProperty(property, value); - } else { - QScopedValueRollback updating(mUpdating, true); - property->setValue(displayValue); - } -} - -void PropertyBrowser::recreateProperty(QtVariantProperty *property, const QVariant &value) -{ - const QString name = property->propertyName(); - const bool wasCurrent = currentItem() && currentItem()->property() == property; - - mCustomPropertiesHelper.deleteProperty(property); - property = addCustomProperty(name, value); - updateCustomPropertyColor(name); - - if (wasCurrent) - setCurrentItem(items(property).constFirst()); -} - -void PropertyBrowser::addProperties() -{ - if (!mObject) - return; - - Q_ASSERT(mUpdating); - - // Add the built-in properties for each object type - switch (mObject->typeId()) { - case Object::MapType: addMapProperties(); break; - case Object::MapObjectType: addMapObjectProperties(); break; - case Object::LayerType: - switch (static_cast(mObject)->layerType()) { - case Layer::TileLayerType: addTileLayerProperties(); break; - case Layer::ObjectGroupType: addObjectGroupProperties(); break; - case Layer::ImageLayerType: addImageLayerProperties(); break; - case Layer::GroupLayerType: addGroupLayerProperties(); break; - } - break; - case Object::TilesetType: addTilesetProperties(); break; - case Object::TileType: addTileProperties(); break; - case Object::WangSetType: addWangSetProperties(); break; - case Object::WangColorType: addWangColorProperties(); break; - case Object::ProjectType: break; - case Object::WorldType: break; - } - - // Make sure certain properties are collapsed, to save space - for (const PropertyId id : { - ColorProperty, - BackgroundColorProperty, - FontProperty, - TintColorProperty, - ImageRectProperty }) { - if (QtProperty *property = mIdToProperty.value(id)) - setExpanded(items(property).constFirst(), false); - } - - // Add a node for the custom properties - mCustomPropertiesGroup = mGroupManager->addProperty(tr("Custom Properties")); - addProperty(mCustomPropertiesGroup); - - updateProperties(); - updateCustomProperties(); -} - -void PropertyBrowser::removeProperties() -{ - Q_ASSERT(mUpdating); - - mCustomPropertiesHelper.clear(); - mVariantManager->clear(); - mGroupManager->clear(); - mPropertyToId.clear(); - mIdToProperty.clear(); - mCustomPropertiesGroup = nullptr; -} - -void PropertyBrowser::updateProperties() -{ - Q_ASSERT(mObject); - - QScopedValueRollback updating(mUpdating, true); - - if (auto classProperty = mIdToProperty.value(ClassProperty)) - classProperty->setValue(mObject->className()); - - switch (mObject->typeId()) { - case Object::MapType: { - const Map *map = static_cast(mObject); - mIdToProperty[WidthProperty]->setValue(map->width()); - mIdToProperty[HeightProperty]->setValue(map->height()); - mIdToProperty[TileWidthProperty]->setValue(map->tileWidth()); - mIdToProperty[TileHeightProperty]->setValue(map->tileHeight()); - mIdToProperty[InfiniteProperty]->setValue(map->infinite()); - mIdToProperty[OrientationProperty]->setValue(map->orientation() - 1); - mIdToProperty[HexSideLengthProperty]->setValue(map->hexSideLength()); - mIdToProperty[StaggerAxisProperty]->setValue(map->staggerAxis()); - mIdToProperty[StaggerIndexProperty]->setValue(map->staggerIndex()); - mIdToProperty[ParallaxOriginProperty]->setValue(map->parallaxOrigin()); - mIdToProperty[LayerFormatProperty]->setValue(mLayerFormatValues.indexOf(map->layerDataFormat())); - mIdToProperty[CompressionLevelProperty]->setValue(map->compressionLevel()); - mIdToProperty[RenderOrderProperty]->setValue(map->renderOrder()); - mIdToProperty[BackgroundColorProperty]->setValue(map->backgroundColor()); - mIdToProperty[ChunkWidthProperty]->setValue(map->chunkSize().width()); - mIdToProperty[ChunkHeightProperty]->setValue(map->chunkSize().height()); - break; - } - case Object::MapObjectType: { - const MapObject *mapObject = static_cast(mObject); - const int flags = mapObjectFlags(mapObject); - - if (mMapObjectFlags != flags) { - UpdatingProperties updatingProperties(this, mUpdating, true); - removeProperties(); - addProperties(); - return; - } - - const QString &className = mapObject->effectiveClassName(); - const auto classColorGroup = mapObject->className().isEmpty() ? QPalette::Disabled - : QPalette::Active; - - FilePath templateFilePath; - if (auto objectTemplate = mapObject->objectTemplate()) - templateFilePath.url = QUrl::fromLocalFile(objectTemplate->fileName()); - - mIdToProperty[IdProperty]->setValue(mapObject->id()); - mIdToProperty[TemplateProperty]->setValue(QVariant::fromValue(templateFilePath)); - mIdToProperty[NameProperty]->setValue(mapObject->name()); - mIdToProperty[ClassProperty]->setValue(className); - mIdToProperty[ClassProperty]->setValueColor(palette().color(classColorGroup, QPalette::WindowText)); - if (auto visibleProperty = mIdToProperty[VisibleProperty]) - visibleProperty->setValue(mapObject->isVisible()); - mIdToProperty[XProperty]->setValue(mapObject->x()); - mIdToProperty[YProperty]->setValue(mapObject->y()); - - if (flags & ObjectHasDimensions) { - mIdToProperty[WidthProperty]->setValue(mapObject->width()); - mIdToProperty[HeightProperty]->setValue(mapObject->height()); - } - - mIdToProperty[RotationProperty]->setValue(mapObject->rotation()); - - if (flags & ObjectHasTile) { - int flippingFlags = 0; - if (mapObject->cell().flippedHorizontally()) - flippingFlags |= 1; - if (mapObject->cell().flippedVertically()) - flippingFlags |= 2; - mIdToProperty[FlippingProperty]->setValue(flippingFlags); - } - - if (flags & ObjectIsText) { - const auto& textData = mapObject->textData(); - mIdToProperty[TextProperty]->setValue(textData.text); - mIdToProperty[FontProperty]->setValue(textData.font); - mIdToProperty[TextAlignmentProperty]->setValue(QVariant::fromValue(textData.alignment)); - mIdToProperty[WordWrapProperty]->setValue(textData.wordWrap); - mIdToProperty[ColorProperty]->setValue(textData.color); - } - break; - } - case Object::LayerType: { - const Layer *layer = static_cast(mObject); - - mIdToProperty[IdProperty]->setValue(layer->id()); - mIdToProperty[NameProperty]->setValue(layer->name()); - mIdToProperty[VisibleProperty]->setValue(layer->isVisible()); - mIdToProperty[LockedProperty]->setValue(layer->isLocked()); - mIdToProperty[OpacityProperty]->setValue(layer->opacity()); - mIdToProperty[TintColorProperty]->setValue(layer->tintColor()); - mIdToProperty[OffsetXProperty]->setValue(layer->offset().x()); - mIdToProperty[OffsetYProperty]->setValue(layer->offset().y()); - mIdToProperty[ParallaxFactorProperty]->setValue(layer->parallaxFactor()); - - switch (layer->layerType()) { - case Layer::TileLayerType: - break; - case Layer::ObjectGroupType: { - const ObjectGroup *objectGroup = static_cast(layer); - const QColor color = objectGroup->color(); - mIdToProperty[ColorProperty]->setValue(color); - mIdToProperty[DrawOrderProperty]->setValue(objectGroup->drawOrder()); - break; - } - case Layer::ImageLayerType: { - const ImageLayer *imageLayer = static_cast(layer); - mIdToProperty[ImageSourceProperty]->setValue(QVariant::fromValue(FilePath { imageLayer->imageSource() })); - mIdToProperty[ColorProperty]->setValue(imageLayer->transparentColor()); - mIdToProperty[RepeatXProperty]->setValue(imageLayer->repeatX()); - mIdToProperty[RepeatYProperty]->setValue(imageLayer->repeatY()); - break; - } - case Layer::GroupLayerType: - break; - } - break; - } - case Object::TilesetType: { - Tileset *tileset = static_cast(mObject); - - if (QtVariantProperty *fileNameProperty = mIdToProperty.value(FileNameProperty)) - fileNameProperty->setValue(QVariant::fromValue(FilePath { QUrl::fromLocalFile(tileset->fileName()) })); - - mIdToProperty[BackgroundColorProperty]->setValue(tileset->backgroundColor()); - - mIdToProperty[NameProperty]->setValue(tileset->name()); - mIdToProperty[ObjectAlignmentProperty]->setValue(tileset->objectAlignment()); - mIdToProperty[TileRenderSizeProperty]->setValue(tileset->tileRenderSize()); - mIdToProperty[FillModeProperty]->setValue(tileset->fillMode()); - mIdToProperty[TileOffsetProperty]->setValue(tileset->tileOffset()); - mIdToProperty[OrientationProperty]->setValue(tileset->orientation()); - mIdToProperty[GridWidthProperty]->setValue(tileset->gridSize().width()); - mIdToProperty[GridHeightProperty]->setValue(tileset->gridSize().height()); - mIdToProperty[ColumnCountProperty]->setValue(tileset->columnCount()); - mIdToProperty[ColumnCountProperty]->setEnabled(mTilesetDocument && tileset->isCollection()); - - if (!tileset->isCollection()) { - mIdToProperty[TilesetImageParametersProperty]->setValue(QVariant::fromValue(mTilesetDocument)); - mIdToProperty[ImageSourceProperty]->setValue(tileset->imageSource().toString(QUrl::PreferLocalFile)); - mIdToProperty[TileWidthProperty]->setValue(tileset->tileWidth()); - mIdToProperty[TileHeightProperty]->setValue(tileset->tileHeight()); - mIdToProperty[MarginProperty]->setValue(tileset->margin()); - mIdToProperty[SpacingProperty]->setValue(tileset->tileSpacing()); - mIdToProperty[ColorProperty]->setValue(tileset->transparentColor()); - } - - const auto flags = tileset->transformationFlags(); - mIdToProperty[AllowFlipHorizontallyProperty]->setValue(flags.testFlag(Tileset::AllowFlipHorizontally)); - mIdToProperty[AllowFlipVerticallyProperty]->setValue(flags.testFlag(Tileset::AllowFlipVertically)); - mIdToProperty[AllowRotateProperty]->setValue(flags.testFlag(Tileset::AllowRotate)); - mIdToProperty[PreferUntransformedProperty]->setValue(flags.testFlag(Tileset::PreferUntransformed)); - break; - } - case Object::TileType: { - const Tile *tile = static_cast(mObject); - const QSize tileSize = tile->size(); - mIdToProperty[IdProperty]->setValue(tile->id()); - mIdToProperty[WidthProperty]->setValue(tileSize.width()); - mIdToProperty[HeightProperty]->setValue(tileSize.height()); - mIdToProperty[TileProbabilityProperty]->setValue(tile->probability()); - if (QtVariantProperty *imageSourceProperty = mIdToProperty.value(ImageSourceProperty)) - imageSourceProperty->setValue(QVariant::fromValue(FilePath { tile->imageSource() })); - mIdToProperty[ImageRectProperty]->setValue(tile->imageRect()); - break; - } - case Object::WangSetType: { - const WangSet *wangSet = static_cast(mObject); - mIdToProperty[NameProperty]->setValue(wangSet->name()); - mIdToProperty[WangSetTypeProperty]->setValue(wangSet->type()); - mIdToProperty[ColorCountProperty]->setValue(wangSet->colorCount()); - break; - } - case Object::WangColorType: { - const WangColor *wangColor = static_cast(mObject); - mIdToProperty[NameProperty]->setValue(wangColor->name()); - mIdToProperty[ColorProperty]->setValue(wangColor->color()); - mIdToProperty[WangColorProbabilityProperty]->setValue(wangColor->probability()); - break; - } - case Object::ProjectType: - break; - case Object::WorldType: - break; - } -} - -Properties PropertyBrowser::combinedProperties() const -{ - Properties combinedProperties; - - // Add properties from selected objects which mObject does not contain to mCombinedProperties. - const auto currentObjects = mDocument->currentObjects(); - for (Object *obj : currentObjects) { - if (obj != mObject) - mergeProperties(combinedProperties, obj->properties()); - } - - if (isAutomappingRulesMap(mMapDocument)) - addAutomappingProperties(combinedProperties, mObject); - - const QString &className = mObject->typeId() == Object::MapObjectType ? static_cast(mObject)->effectiveClassName() - : mObject->className(); - - // Inherit properties from the class - if (auto type = Object::propertyTypes().findClassFor(className, *mObject)) - mergeProperties(combinedProperties, type->members); - - if (mObject->typeId() == Object::MapObjectType) { - auto mapObject = static_cast(mObject); - - // Inherit properties from the tile - if (const Tile *tile = mapObject->cell().tile()) - mergeProperties(combinedProperties, tile->properties()); - - // Inherit properties from the template - if (const MapObject *templateObject = mapObject->templateObject()) - mergeProperties(combinedProperties, templateObject->properties()); - } - - mergeProperties(combinedProperties, mObject->properties()); - - return combinedProperties; -} - -void PropertyBrowser::updateCustomProperties() -{ - if (!mObject) - return; - - UpdatingProperties updatingProperties(this, mUpdating); - - mCustomPropertiesHelper.clear(); - - QMapIterator it(combinedProperties()); - while (it.hasNext()) { - it.next(); - - QtVariantProperty *property = createCustomProperty(it.key(), it.value()); - mCustomPropertiesGroup->addSubProperty(property); - - // Collapse custom color properties, to save space - if (property->valueType() == QMetaType::QColor) - setExpanded(items(property).constFirst(), false); - - updateCustomPropertyColor(it.key()); - } -} - -void PropertyBrowser::updateCustomPropertyColor(const QString &name) -{ - if (QtVariantProperty *property = mCustomPropertiesHelper.property(name)) - updateCustomPropertyColor(property); -} - -void PropertyBrowser::updateCustomPropertyColors() -{ - for (QtVariantProperty *property : mCustomPropertiesHelper.properties()) - updateCustomPropertyColor(property); -} - -// If there are other objects selected check if their properties are equal. If not give them a gray color. -void PropertyBrowser::updateCustomPropertyColor(QtVariantProperty *property) -{ - if (!property->isEnabled()) - return; - - const QString propertyName = property->propertyName(); - const QString propertyValue = property->valueText(); - - const auto &objects = mDocument->currentObjects(); - - const QPalette palette = QGuiApplication::palette(); - const QColor textColor = palette.color(QPalette::Active, QPalette::WindowText); - const QColor disabledTextColor = palette.color(QPalette::Disabled, QPalette::WindowText); - - // If one of the objects doesn't have this property then gray out the name and value. - for (Object *obj : objects) { - if (!obj->hasProperty(propertyName)) { - property->setNameColor(disabledTextColor); - property->setValueColor(disabledTextColor); - return; - } - } - - // If one of the objects doesn't have the same property value then gray out the value. - for (Object *obj : objects) { - if (obj == mObject) - continue; - if (obj->property(propertyName) != propertyValue) { - property->setNameColor(textColor); - property->setValueColor(disabledTextColor); - return; - } - } - - property->setNameColor(textColor); - property->setValueColor(textColor); -} - -QVariant PropertyBrowser::toDisplayValue(QVariant value) const -{ - return mCustomPropertiesHelper.toDisplayValue(value); -} - -QVariant PropertyBrowser::fromDisplayValue(QtProperty *property, QVariant value) const -{ - return mCustomPropertiesHelper.fromDisplayValue(property, value); -} - -void PropertyBrowser::retranslateUi() -{ - mStaggerAxisNames.clear(); - mStaggerAxisNames.append(tr("X")); - mStaggerAxisNames.append(tr("Y")); - - mStaggerIndexNames.clear(); - mStaggerIndexNames.append(tr("Odd")); - mStaggerIndexNames.append(tr("Even")); - - mOrientationNames.clear(); - mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Orthogonal")); - mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Isometric")); - mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Isometric (Staggered)")); - mOrientationNames.append(QCoreApplication::translate("Tiled::NewMapDialog", "Hexagonal (Staggered)")); - - mTilesetOrientationNames.clear(); - mTilesetOrientationNames.append(mOrientationNames.at(0)); - mTilesetOrientationNames.append(mOrientationNames.at(1)); - - mTileRenderSizeNames.clear(); - mTileRenderSizeNames.append(tr("Tile Size")); - mTileRenderSizeNames.append(tr("Map Grid Size")); - - mFillModeNames.clear(); - mFillModeNames.append(tr("Stretch")); - mFillModeNames.append(tr("Preserve Aspect Ratio")); - - mLayerFormatNames.clear(); - mLayerFormatValues.clear(); - - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "XML (deprecated)")); - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (uncompressed)")); - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (gzip compressed)")); - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (zlib compressed)")); - - mLayerFormatValues.append(Map::XML); - mLayerFormatValues.append(Map::Base64); - mLayerFormatValues.append(Map::Base64Gzip); - mLayerFormatValues.append(Map::Base64Zlib); - - if (compressionSupported(Zstandard)) { - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "Base64 (Zstandard compressed)")); - mLayerFormatValues.append(Map::Base64Zstandard); - } - - mLayerFormatNames.append(QCoreApplication::translate("PreferencesDialog", "CSV")); - mLayerFormatValues.append(Map::CSV); - - mRenderOrderNames.clear(); - mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Right Down")); - mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Right Up")); - mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Left Down")); - mRenderOrderNames.append(QCoreApplication::translate("PreferencesDialog", "Left Up")); - - mAlignmentNames.clear(); - mAlignmentNames.append(tr("Unspecified")); - mAlignmentNames.append(tr("Top Left")); - mAlignmentNames.append(tr("Top")); - mAlignmentNames.append(tr("Top Right")); - mAlignmentNames.append(tr("Left")); - mAlignmentNames.append(tr("Center")); - mAlignmentNames.append(tr("Right")); - mAlignmentNames.append(tr("Bottom Left")); - mAlignmentNames.append(tr("Bottom")); - mAlignmentNames.append(tr("Bottom Right")); - - mFlippingFlagNames.clear(); - mFlippingFlagNames.append(tr("Horizontal")); - mFlippingFlagNames.append(tr("Vertical")); - - mDrawOrderNames.clear(); - mDrawOrderNames.append(tr("Top Down")); - mDrawOrderNames.append(tr("Manual")); - - mWangSetTypeNames.clear(); - mWangSetTypeNames.append(tr("Corner")); - mWangSetTypeNames.append(tr("Edge")); - mWangSetTypeNames.append(tr("Mixed")); - - UpdatingProperties updatingProperties(this, mUpdating); - removeProperties(); - addProperties(); -} - -} // namespace Tiled - -#include "moc_propertybrowser.cpp" diff --git a/src/tiled/propertybrowser.h b/src/tiled/propertybrowser.h deleted file mode 100644 index ead7b6f2aa..0000000000 --- a/src/tiled/propertybrowser.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * propertybrowser.h - * Copyright 2013-2021, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once - -#include "changeevents.h" -#include "custompropertieshelper.h" -#include "map.h" -#include "properties.h" - -#include - -#include - -class QUndoCommand; - -class QtGroupPropertyManager; -class QtVariantProperty; -class QtVariantPropertyManager; - -namespace Tiled { - -class GroupLayer; -class ImageLayer; -class MapObject; -class ObjectGroup; -class Tile; -class TileLayer; -class Tileset; - -class Document; -class MapDocument; -class TilesetDocument; - -class PropertyBrowser : public QtTreePropertyBrowser -{ - Q_OBJECT - -public: - explicit PropertyBrowser(QWidget *parent = nullptr); - - void setObject(Object *object); - Object *object() const; - - void setDocument(Document *document); - - bool isCustomPropertyItem(const QtBrowserItem *item) const; - bool allCustomPropertyItems(const QList &items) const; - - void selectCustomProperty(const QString &name); - void editCustomProperty(const QString &name); - - QSize sizeHint() const override; - -protected: - bool event(QEvent *event) override; - -private: - void documentChanged(const ChangeEvent &change); - void mapChanged(); - void mapObjectsChanged(const MapObjectsChangeEvent &mapObjectsChange); - void tilesetChanged(Tileset *tileset); - void tileChanged(Tile *tile); - void tileTypeChanged(Tile *tile); - void wangSetChanged(WangSet *wangSet); - - void propertyAdded(Object *object, const QString &name); - void propertyRemoved(Object *object, const QString &name); - void propertyChanged(Object *object, const QString &name); - void propertiesChanged(Object *object); - void selectedObjectsChanged(); - void selectedLayersChanged(); - void selectedTilesChanged(); - - void propertyTypesChanged(); - - void valueChanged(QtProperty *property, const QVariant &val); - void customPropertyValueChanged(const QStringList &path, const QVariant &value); - - void resetProperty(QtProperty *property); - - enum PropertyId { - NameProperty, - ClassProperty, - XProperty, - YProperty, - WidthProperty, - HeightProperty, - RotationProperty, - VisibleProperty, - LockedProperty, - OpacityProperty, - TextProperty, - TextAlignmentProperty, - FontProperty, - WordWrapProperty, - OffsetXProperty, - OffsetYProperty, - ParallaxFactorProperty, - RepeatXProperty, - RepeatYProperty, - ColorProperty, - BackgroundColorProperty, - TileWidthProperty, - TileHeightProperty, - GridWidthProperty, - GridHeightProperty, - OrientationProperty, - HexSideLengthProperty, - StaggerAxisProperty, - StaggerIndexProperty, - ParallaxOriginProperty, - RenderOrderProperty, - LayerFormatProperty, - ImageSourceProperty, - ImageRectProperty, - TilesetImageParametersProperty, - FlippingProperty, - DrawOrderProperty, - FileNameProperty, - ObjectAlignmentProperty, - TileRenderSizeProperty, - FillModeProperty, - TileOffsetProperty, - MarginProperty, - SpacingProperty, - TileProbabilityProperty, - ColumnCountProperty, - IdProperty, - ColorCountProperty, - WangColorProbabilityProperty, - WangSetTypeProperty, - InfiniteProperty, - TemplateProperty, - CompressionLevelProperty, - ChunkWidthProperty, - ChunkHeightProperty, - TintColorProperty, - AllowFlipHorizontallyProperty, - AllowFlipVerticallyProperty, - AllowRotateProperty, - PreferUntransformedProperty, - }; - - void addMapProperties(); - void addMapObjectProperties(); - void addLayerProperties(QtProperty *parent); - void addTileLayerProperties(); - void addObjectGroupProperties(); - void addImageLayerProperties(); - void addGroupLayerProperties(); - void addTilesetProperties(); - void addTileProperties(); - void addWangSetProperties(); - void addWangColorProperties(); - - QtVariantProperty *addClassProperty(QtProperty *parent); - - void applyMapValue(PropertyId id, const QVariant &val); - void applyMapObjectValue(PropertyId id, const QVariant &val); - QUndoCommand *applyMapObjectValueTo(PropertyId id, const QVariant &val, MapObject *mapObject); - void applyLayerValue(PropertyId id, const QVariant &val); - QUndoCommand *applyTileLayerValueTo(PropertyId id, const QVariant &val, QList tileLayers); - QUndoCommand *applyObjectGroupValueTo(PropertyId id, const QVariant &val, QList objectGroups); - QUndoCommand *applyImageLayerValueTo(PropertyId id, const QVariant &val, QList imageLayers); - QUndoCommand *applyGroupLayerValueTo(PropertyId id, const QVariant &val, QList groupLayers); - void applyTilesetValue(PropertyId id, const QVariant &val); - void applyTileValue(PropertyId id, const QVariant &val); - void applyWangSetValue(PropertyId id, const QVariant &val); - void applyWangColorValue(PropertyId id, const QVariant &val); - - QtVariantProperty *createProperty(PropertyId id, - int type, - const QString &name); - QtVariantProperty *createCustomProperty(const QString &name, - const QVariant &value); - - using QtTreePropertyBrowser::addProperty; - QtVariantProperty *addProperty(PropertyId id, - int type, - const QString &name, - QtProperty *parent); - - QtVariantProperty *addCustomProperty(const QString &name, const QVariant &value); - void setCustomPropertyValue(QtVariantProperty *property, const QVariant &value); - void recreateProperty(QtVariantProperty *property, const QVariant &value); - - void addProperties(); - void removeProperties(); - void updateProperties(); - Properties combinedProperties() const; - void updateCustomProperties(); - - void updateCustomPropertyColor(const QString &name); - void updateCustomPropertyColors(); - void updateCustomPropertyColor(QtVariantProperty *property); - - QVariant toDisplayValue(QVariant value) const; - QVariant fromDisplayValue(QtProperty *property, QVariant value) const; - - void retranslateUi(); - - bool mUpdating = false; - int mMapObjectFlags = 0; - Object *mObject = nullptr; - Document *mDocument = nullptr; - MapDocument *mMapDocument = nullptr; - TilesetDocument *mTilesetDocument = nullptr; - - QtVariantPropertyManager *mVariantManager; - QtGroupPropertyManager *mGroupManager; - QtProperty *mCustomPropertiesGroup; - - QHash mPropertyToId; - QHash mIdToProperty; - CustomPropertiesHelper mCustomPropertiesHelper; - - QStringList mStaggerAxisNames; - QStringList mStaggerIndexNames; - QStringList mOrientationNames; - QStringList mTilesetOrientationNames; - QStringList mTileRenderSizeNames; - QStringList mFillModeNames; - QStringList mLayerFormatNames; - QList mLayerFormatValues; - QStringList mRenderOrderNames; - QStringList mAlignmentNames; - QStringList mFlippingFlagNames; - QStringList mDrawOrderNames; - QStringList mWangSetTypeNames; - QMap mWangSetIcons; -}; - -/** - * Returns the object for which the properties are displayed. - */ -inline Object *PropertyBrowser::object() const -{ - return mObject; -} - -} // namespace Tiled From d294ece6835752e395d2522e7db598a9aa19a171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 8 Nov 2024 20:18:50 +0100 Subject: [PATCH 60/78] Used the new properties framework in the Project Properties dialog A bit of a hacky solution to hiding the "Custom Properties" header in this context, because it is now inside a group box with that title. --- src/tiled/projectpropertiesdialog.cpp | 121 ++++++++++++++++---------- src/tiled/projectpropertiesdialog.h | 17 ++-- src/tiled/projectpropertiesdialog.ui | 28 ++---- src/tiled/propertieswidget.cpp | 7 +- src/tiled/propertieswidget.h | 4 + src/tiled/varianteditor.cpp | 18 ++++ src/tiled/varianteditor.h | 7 +- 7 files changed, 126 insertions(+), 76 deletions(-) diff --git a/src/tiled/projectpropertiesdialog.cpp b/src/tiled/projectpropertiesdialog.cpp index 304abb8d85..ec2ae91e71 100644 --- a/src/tiled/projectpropertiesdialog.cpp +++ b/src/tiled/projectpropertiesdialog.cpp @@ -24,66 +24,90 @@ #include "mapformat.h" #include "project.h" #include "projectdocument.h" -#include "varianteditorfactory.h" -#include "variantpropertymanager.h" +#include "tiled.h" +#include "utils.h" +#include "varianteditor.h" -#include +#include +#include namespace Tiled { +template<> EnumData enumData() +{ + return {{ + QCoreApplication::translate("Tiled::ProjectPropertiesDialog", "Tiled 1.8"), + QCoreApplication::translate("Tiled::ProjectPropertiesDialog", "Tiled 1.9"), + QCoreApplication::translate("Tiled::ProjectPropertiesDialog", "Tiled 1.10"), + QCoreApplication::translate("Tiled::ProjectPropertiesDialog", "Latest"), + }, { + Tiled_1_8, + Tiled_1_9, + Tiled_1_10, + Tiled_Latest, + }}; +} + ProjectPropertiesDialog::ProjectPropertiesDialog(Project &project, QWidget *parent) : QDialog(parent) , ui(new Ui::ProjectPropertiesDialog) , mProject(project) - , mPropertiesProjectDocument(new ProjectDocument(std::make_unique(), this)) + , mLocalProjectDocument(new ProjectDocument(std::make_unique(project), this)) { ui->setupUi(this); - mPropertiesProjectDocument->project().setProperties(project.properties()); - - auto variantPropertyManager = new VariantPropertyManager(this); - auto variantEditorFactory = new VariantEditorFactory(this); - auto groupPropertyManager = new QtGroupPropertyManager(this); - - ui->propertyBrowser->setFactoryForManager(variantPropertyManager, - variantEditorFactory); - - const QMap versionToName { - { Tiled_1_8, tr("Tiled 1.8") }, - { Tiled_1_9, tr("Tiled 1.9") }, - { Tiled_1_10, tr("Tiled 1.10") }, - { Tiled_Latest, tr("Latest") }, - }; - mVersions = versionToName.keys(); - - mCompatibilityVersionProperty = variantPropertyManager->addProperty(QtVariantPropertyManager::enumTypeId(), - tr("Compatibility Version")); - mCompatibilityVersionProperty->setAttribute(QLatin1String("enumNames"), - QVariant::fromValue(versionToName.values())); - mCompatibilityVersionProperty->setValue(mVersions.indexOf(project.mCompatibilityVersion)); - - mExtensionPathProperty = variantPropertyManager->addProperty(filePathTypeId(), tr("Extensions Directory")); - mExtensionPathProperty->setValue(project.mExtensionsPath); - mExtensionPathProperty->setAttribute(QStringLiteral("directory"), true); + mCompatibilityVersionProperty = new EnumProperty( + tr("Compatibility Version"), + [=] { + return localProject().mCompatibilityVersion; + }, + [=](CompatibilityVersion value) { + localProject().mCompatibilityVersion = value; + }); + + mExtensionPathProperty = new UrlProperty( + tr("Extensions Directory"), + [=] { + return QUrl::fromLocalFile(localProject().mExtensionsPath); + }, + [=](const QUrl &value) { + localProject().mExtensionsPath = value.toLocalFile(); + }); + mExtensionPathProperty->setIsDirectory(true); QString ruleFileFilter = QCoreApplication::translate("File Types", "Automapping Rules files (*.txt)"); FormatHelper helper(FileFormat::ReadWrite, std::move(ruleFileFilter)); - mAutomappingRulesFileProperty = variantPropertyManager->addProperty(filePathTypeId(), tr("Automapping rules")); - mAutomappingRulesFileProperty->setValue(project.mAutomappingRulesFile); - mAutomappingRulesFileProperty->setAttribute(QStringLiteral("filter"), helper.filter()); + mAutomappingRulesFileProperty = new UrlProperty( + tr("Automapping rules"), + [=] { + return QUrl::fromLocalFile(localProject().mAutomappingRulesFile); + }, + [=](const QUrl &value) { + localProject().mAutomappingRulesFile = value.toLocalFile(); + }); + mAutomappingRulesFileProperty->setFilter(helper.filter()); + + auto generalGroup = new QGroupBox(tr("General")); + auto generalLayout = new QFormLayout(generalGroup); + generalLayout->addRow(mCompatibilityVersionProperty->name(), mCompatibilityVersionProperty->createEditor(generalGroup)); - auto generalGroupProperty = groupPropertyManager->addProperty(tr("General")); - generalGroupProperty->addSubProperty(mCompatibilityVersionProperty); + auto filesGroup = new QGroupBox(tr("Paths && Files")); + auto filesLayout = new QFormLayout(filesGroup); + filesLayout->addRow(mExtensionPathProperty->name(), mExtensionPathProperty->createEditor(filesGroup)); + filesLayout->addRow(mAutomappingRulesFileProperty->name(), mAutomappingRulesFileProperty->createEditor(filesGroup)); - auto filesGroupProperty = groupPropertyManager->addProperty(tr("Paths && Files")); - filesGroupProperty->addSubProperty(mExtensionPathProperty); - filesGroupProperty->addSubProperty(mAutomappingRulesFileProperty); + ui->dialogLayout->insertWidget(0, filesGroup); + ui->dialogLayout->insertWidget(0, generalGroup); - ui->propertyBrowser->addProperty(generalGroupProperty); - ui->propertyBrowser->addProperty(filesGroupProperty); + // Don't display the "Custom Properties" header + ui->propertiesWidget->customPropertiesGroup()->setName(QString()); - ui->propertiesWidget->setDocument(mPropertiesProjectDocument); + // Tweak margins + const auto margin = Utils::dpiScaled(3); + ui->propertiesWidget->propertiesView()->widget()->setContentsMargins(0, margin, 0, margin); + + ui->propertiesWidget->setDocument(mLocalProjectDocument); } ProjectPropertiesDialog::~ProjectPropertiesDialog() @@ -93,14 +117,21 @@ ProjectPropertiesDialog::~ProjectPropertiesDialog() void ProjectPropertiesDialog::accept() { - mProject.setProperties(mPropertiesProjectDocument->project().properties()); - mProject.mCompatibilityVersion = mVersions.at(mCompatibilityVersionProperty->value().toInt()); - mProject.mExtensionsPath = mExtensionPathProperty->value().toString(); - mProject.mAutomappingRulesFile = mAutomappingRulesFileProperty->value().toString(); + auto &project = localProject(); + + mProject.setProperties(project.properties()); + mProject.mCompatibilityVersion = project.mCompatibilityVersion; + mProject.mExtensionsPath = project.mExtensionsPath; + mProject.mAutomappingRulesFile = project.mAutomappingRulesFile; QDialog::accept(); } +Project &ProjectPropertiesDialog::localProject() +{ + return mLocalProjectDocument->project(); +} + } // namespace Tiled #include "moc_projectpropertiesdialog.cpp" diff --git a/src/tiled/projectpropertiesdialog.h b/src/tiled/projectpropertiesdialog.h index dbfd4e59d9..5ba34f905b 100644 --- a/src/tiled/projectpropertiesdialog.h +++ b/src/tiled/projectpropertiesdialog.h @@ -20,20 +20,18 @@ #pragma once -#include "tiled.h" - #include -class QtVariantProperty; - namespace Ui { class ProjectPropertiesDialog; } namespace Tiled { +class IntProperty; class Project; class ProjectDocument; +class UrlProperty; class ProjectPropertiesDialog : public QDialog { @@ -46,14 +44,15 @@ class ProjectPropertiesDialog : public QDialog void accept() override; private: + Project &localProject(); + Ui::ProjectPropertiesDialog *ui; Project &mProject; - ProjectDocument *mPropertiesProjectDocument; - QList mVersions; - QtVariantProperty *mCompatibilityVersionProperty; - QtVariantProperty *mExtensionPathProperty; - QtVariantProperty *mAutomappingRulesFileProperty; + ProjectDocument *mLocalProjectDocument; + IntProperty *mCompatibilityVersionProperty; + UrlProperty *mExtensionPathProperty; + UrlProperty *mAutomappingRulesFileProperty; }; } // namespace Tiled diff --git a/src/tiled/projectpropertiesdialog.ui b/src/tiled/projectpropertiesdialog.ui index 899db3ac9c..1e94e909b8 100644 --- a/src/tiled/projectpropertiesdialog.ui +++ b/src/tiled/projectpropertiesdialog.ui @@ -7,25 +7,19 @@ 0 0 586 - 356 + 489 Project Properties - + - - - - - - - 0 - 1 - + + + Custom Properties - + @@ -35,22 +29,16 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok - - QtGroupBoxPropertyBrowser - QWidget -
QtGroupBoxPropertyBrowser
- 1 -
Tiled::PropertiesWidget QTreeView diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 6275a4b66b..38d0188171 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2159,7 +2159,7 @@ class WangColorProperties : public ObjectProperties PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} - , mCustomProperties(new CustomProperties) + , mCustomProperties(new CustomProperties(this)) , mPropertyBrowser(new VariantEditorView(this)) { mActionAddProperty = new QAction(this); @@ -2251,6 +2251,11 @@ void PropertiesWidget::setDocument(Document *document) } } +GroupProperty *PropertiesWidget::customPropertiesGroup() const +{ + return mCustomProperties; +} + void PropertiesWidget::selectCustomProperty(const QString &name) { // mPropertyBrowser->selectCustomProperty(name); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 7c2969be15..2c9a5f0432 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -31,6 +31,7 @@ class Object; class CustomProperties; class Document; +class GroupProperty; class ObjectProperties; class VariantEditorView; @@ -52,6 +53,9 @@ class PropertiesWidget : public QWidget */ void setDocument(Document *document); + GroupProperty *customPropertiesGroup() const; + VariantEditorView *propertiesView() const { return mPropertyBrowser; } + signals: void bringToFront(); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index c9f9839408..f4caec5bce 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -40,6 +40,14 @@ namespace Tiled { +void Property::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(name); + } +} + void Property::setToolTip(const QString &toolTip) { if (m_toolTip != toolTip) { @@ -158,6 +166,7 @@ QWidget *UrlProperty::createEditor(QWidget *parent) { auto editor = new FileEdit(parent); editor->setFilter(m_filter); + editor->setIsDirectory(m_isDirectory); auto syncEditor = [=] { editor->setFileUrl(value()); @@ -737,6 +746,9 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) updatePropertyToolTip(widgets, property->toolTip()); updatePropertyActions(widgets, property->actions()); + connect(property, &Property::nameChanged, this, [=] (const QString &name) { + updatePropertyName(m_propertyWidgets[property], name); + }); connect(property, &Property::enabledChanged, this, [=] (bool enabled) { updatePropertyEnabled(m_propertyWidgets[property], enabled); }); @@ -787,6 +799,12 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo } } +void VariantEditor::updatePropertyName(const PropertyWidgets &widgets, const QString &name) +{ + if (widgets.label) + widgets.label->setText(name); +} + void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) { if (widgets.label) diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index d5abef9492..f1c9c2b84c 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -44,7 +44,7 @@ class PropertyLabel; class Property : public QObject { Q_OBJECT - Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged) @@ -72,6 +72,7 @@ class Property : public QObject {} const QString &name() const { return m_name; } + void setName(const QString &name); const QString &toolTip() const { return m_toolTip; } void setToolTip(const QString &toolTip); @@ -90,6 +91,7 @@ class Property : public QObject virtual QWidget *createEditor(QWidget *parent) = 0; signals: + void nameChanged(const QString &name); void toolTipChanged(const QString &toolTip); void valueChanged(); void enabledChanged(bool enabled); @@ -256,8 +258,10 @@ struct UrlProperty : PropertyTemplate using PropertyTemplate::PropertyTemplate; QWidget *createEditor(QWidget *parent) override; void setFilter(const QString &filter) { m_filter = filter; } + void setIsDirectory(bool isDirectory) { m_isDirectory = isDirectory; } private: QString m_filter; + bool m_isDirectory = false; }; struct IntProperty : PropertyTemplate @@ -523,6 +527,7 @@ class VariantEditor : public QWidget void setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded); + void updatePropertyName(const PropertyWidgets &widgets, const QString &name); void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); From e500940e55fd5215c61dcec414396e9432d42f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 8 Nov 2024 21:04:58 +0100 Subject: [PATCH 61/78] Removed now unused VariantEditorFactory and VariantPropertyManager These have been replaced by the new property framework. --- src/tiled/libtilededitor.qbs | 5 - src/tiled/objectrefdialog.cpp | 8 + src/tiled/objectrefdialog.h | 20 +- src/tiled/objectrefedit.cpp | 1 - src/tiled/objectrefedit.h | 2 +- src/tiled/objectselectionitem.cpp | 2 +- src/tiled/pch.h | 2 - src/tiled/stylehelper.cpp | 4 - src/tiled/varianteditorfactory.cpp | 378 ------------------- src/tiled/varianteditorfactory.h | 92 ----- src/tiled/variantmapproperty.cpp | 1 - src/tiled/variantpropertymanager.cpp | 530 --------------------------- src/tiled/variantpropertymanager.h | 137 ------- tiled.qbs | 1 - 14 files changed, 29 insertions(+), 1154 deletions(-) delete mode 100644 src/tiled/varianteditorfactory.cpp delete mode 100644 src/tiled/varianteditorfactory.h delete mode 100644 src/tiled/variantpropertymanager.cpp delete mode 100644 src/tiled/variantpropertymanager.h diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 15ddd80f1c..328af5f4d7 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -7,7 +7,6 @@ DynamicLibrary { Depends { name: "libtiled" } Depends { name: "translations" } - Depends { name: "qtpropertybrowser" } Depends { name: "qtsingleapplication" } Depends { name: "Qt"; submodules: ["core", "widgets", "concurrent", "qml", "svg"]; versionAtLeast: "5.12" } Depends { name: "Qt.openglwidgets"; condition: Qt.core.versionMajor >= 6; required: false } @@ -552,12 +551,8 @@ DynamicLibrary { "utils.h", "varianteditor.cpp", "varianteditor.h", - "varianteditorfactory.cpp", - "varianteditorfactory.h", "variantmapproperty.cpp", "variantmapproperty.h", - "variantpropertymanager.cpp", - "variantpropertymanager.h", "wangbrush.cpp", "wangbrush.h", "wangcolormodel.cpp", diff --git a/src/tiled/objectrefdialog.cpp b/src/tiled/objectrefdialog.cpp index 22d5599ac2..9751b4af24 100644 --- a/src/tiled/objectrefdialog.cpp +++ b/src/tiled/objectrefdialog.cpp @@ -38,6 +38,14 @@ namespace Tiled { +MapObject *DisplayObjectRef::object() const +{ + if (!mapDocument || ref.id <= 0) + return nullptr; + return mapDocument->map()->findObjectById(ref.id); +} + + class ImmutableMapObjectProxyModel : public ReversingProxyModel { public: diff --git a/src/tiled/objectrefdialog.h b/src/tiled/objectrefdialog.h index 5959e27875..9dd39ea8a2 100644 --- a/src/tiled/objectrefdialog.h +++ b/src/tiled/objectrefdialog.h @@ -21,7 +21,6 @@ #pragma once #include "properties.h" -#include "variantpropertymanager.h" #include #include @@ -37,6 +36,25 @@ class MapDocument; class ImmutableMapObjectProxyModel; class Tile; +class DisplayObjectRef +{ +public: + explicit DisplayObjectRef(ObjectRef ref = ObjectRef(), + MapDocument *mapDocument = nullptr) + : ref(ref) + , mapDocument(mapDocument) + {} + + bool operator==(const DisplayObjectRef &o) const + { return ref.id == o.ref.id && mapDocument == o.mapDocument; } + + int id() const { return ref.id; } + MapObject *object() const; + + ObjectRef ref; + MapDocument *mapDocument; +}; + class ObjectsTreeView : public QTreeView { Q_OBJECT diff --git a/src/tiled/objectrefedit.cpp b/src/tiled/objectrefedit.cpp index 5927f4f996..f26a7060ce 100644 --- a/src/tiled/objectrefedit.cpp +++ b/src/tiled/objectrefedit.cpp @@ -23,7 +23,6 @@ #include "mapdocument.h" #include "mapobject.h" -#include "objectrefdialog.h" #include "utils.h" #include diff --git a/src/tiled/objectrefedit.h b/src/tiled/objectrefedit.h index ebcf94bdfb..628fc640d0 100644 --- a/src/tiled/objectrefedit.h +++ b/src/tiled/objectrefedit.h @@ -20,7 +20,7 @@ #pragma once -#include "variantpropertymanager.h" +#include "objectrefdialog.h" #include diff --git a/src/tiled/objectselectionitem.cpp b/src/tiled/objectselectionitem.cpp index 572e6650d2..38c033356e 100644 --- a/src/tiled/objectselectionitem.cpp +++ b/src/tiled/objectselectionitem.cpp @@ -29,11 +29,11 @@ #include "maprenderer.h" #include "mapscene.h" #include "objectgroup.h" +#include "objectrefdialog.h" #include "objectreferenceitem.h" #include "preferences.h" #include "tile.h" #include "utils.h" -#include "variantpropertymanager.h" #include #include diff --git a/src/tiled/pch.h b/src/tiled/pch.h index 4c2f93d024..3db4ff3c2f 100644 --- a/src/tiled/pch.h +++ b/src/tiled/pch.h @@ -40,8 +40,6 @@ #include #include -#include - // libtiled #include #include diff --git a/src/tiled/stylehelper.cpp b/src/tiled/stylehelper.cpp index ea44724c64..43e4489754 100644 --- a/src/tiled/stylehelper.cpp +++ b/src/tiled/stylehelper.cpp @@ -29,8 +29,6 @@ #include #include -#include - namespace Tiled { StyleHelper *StyleHelper::mInstance; @@ -163,8 +161,6 @@ void StyleHelper::apply() style->setPalette(desiredPalette); } - QtBoolPropertyManager::resetIcons(); - emit styleApplied(); } diff --git a/src/tiled/varianteditorfactory.cpp b/src/tiled/varianteditorfactory.cpp deleted file mode 100644 index e63040dedb..0000000000 --- a/src/tiled/varianteditorfactory.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - * varianteditorfactory.cpp - * Copyright (C) 2006 Trolltech ASA. All rights reserved. (GPLv2) - * Copyright 2013-2021, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "varianteditorfactory.h" - -#include "fileedit.h" -#include "objectrefedit.h" -#include "textpropertyedit.h" -#include "tilesetdocument.h" -#include "tilesetparametersedit.h" -#include "utils.h" -#include "variantpropertymanager.h" - -#include -#include -#include - -namespace Tiled { - -class ResetWidget : public QWidget -{ - Q_OBJECT - -public: - ResetWidget(QtProperty *property, QWidget *editor, QWidget *parent = nullptr); - -signals: - void resetProperty(QtProperty *property); - -private: - void buttonClicked(); - - QtProperty *mProperty; -}; - -ResetWidget::ResetWidget(QtProperty *property, QWidget *editor, QWidget *parent) - : QWidget(parent) - , mProperty(property) -{ - auto layout = new QHBoxLayout(this); - - auto resetButton = new QToolButton(this); - resetButton->setIcon(QIcon(QLatin1String(":/images/16/edit-clear.png"))); - resetButton->setIconSize(Utils::smallIconSize()); - resetButton->setAutoRaise(true); - resetButton->setToolTip(tr("Reset")); - Utils::setThemeIcon(resetButton, "edit-clear"); - - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(editor); - layout->addWidget(resetButton); - - setFocusProxy(editor); - - connect(resetButton, &QToolButton::clicked, this, &ResetWidget::buttonClicked); -} - -void ResetWidget::buttonClicked() -{ - emit resetProperty(mProperty); -} - - -VariantEditorFactory::~VariantEditorFactory() -{ - // Using QMap::keys here is important because the maps get modified in - // slotEditorDestroyed. - qDeleteAll(mFileEditToProperty.keys()); - qDeleteAll(mTilesetEditToProperty.keys()); - qDeleteAll(mTextPropertyEditToProperty.keys()); - qDeleteAll(mObjectRefEditToProperty.keys()); - qDeleteAll(mComboBoxToProperty.keys()); -} - -void VariantEditorFactory::connectPropertyManager(QtVariantPropertyManager *manager) -{ - connect(manager, &QtVariantPropertyManager::valueChanged, - this, &VariantEditorFactory::slotPropertyChanged); - connect(manager, &QtVariantPropertyManager::attributeChanged, - this, &VariantEditorFactory::slotPropertyAttributeChanged); - QtVariantEditorFactory::connectPropertyManager(manager); -} - -QWidget *VariantEditorFactory::createEditor(QtVariantPropertyManager *manager, - QtProperty *property, - QWidget *parent) -{ - const int type = manager->propertyType(property); - QWidget *editor = nullptr; - - if (type == filePathTypeId()) { - auto fileEdit = new FileEdit(parent); - FilePath filePath = manager->value(property).value(); - fileEdit->setFileUrl(filePath.url); - fileEdit->setFilter(manager->attributeValue(property, QLatin1String("filter")).toString()); - fileEdit->setIsDirectory(manager->attributeValue(property, QLatin1String("directory")).toBool()); - mCreatedFileEdits[property].append(fileEdit); - mFileEditToProperty[fileEdit] = property; - - connect(fileEdit, &FileEdit::fileUrlChanged, - this, &VariantEditorFactory::fileEditFileUrlChanged); - connect(fileEdit, &QObject::destroyed, - this, &VariantEditorFactory::slotEditorDestroyed); - - editor = fileEdit; - } else if (type == VariantPropertyManager::displayObjectRefTypeId()) { - auto objectRefEdit = new ObjectRefEdit(parent); - auto objectRef = manager->value(property).value(); - objectRefEdit->setValue(objectRef); - mCreatedObjectRefEdits[property].append(objectRefEdit); - mObjectRefEditToProperty[objectRefEdit] = property; - - connect(objectRefEdit, &ObjectRefEdit::valueChanged, - this, &VariantEditorFactory::objectRefEditValueChanged); - connect(objectRefEdit, &QObject::destroyed, - this, &VariantEditorFactory::slotEditorDestroyed); - - editor = objectRefEdit; - } else if (type == VariantPropertyManager::tilesetParametersTypeId()) { - auto tilesetEdit = new TilesetParametersEdit(parent); - tilesetEdit->setTilesetDocument(manager->value(property).value()); - mCreatedTilesetEdits[property].append(tilesetEdit); - mTilesetEditToProperty[tilesetEdit] = property; - - connect(tilesetEdit, &QObject::destroyed, - this, &VariantEditorFactory::slotEditorDestroyed); - - editor = tilesetEdit; - } else if (type == QMetaType::QString) { - bool multiline = manager->attributeValue(property, QLatin1String("multiline")).toBool(); - QStringList suggestions = manager->attributeValue(property, QLatin1String("suggestions")).toStringList(); - - if (multiline) { - auto textEdit = new TextPropertyEdit(parent); - textEdit->setText(manager->value(property).toString()); - mCreatedTextPropertyEdits[property].append(textEdit); - mTextPropertyEditToProperty[textEdit] = property; - - connect(textEdit, &TextPropertyEdit::textChanged, - this, &VariantEditorFactory::textPropertyEditTextChanged); - connect(textEdit, &QObject::destroyed, - this, &VariantEditorFactory::slotEditorDestroyed); - - editor = textEdit; - } else if (!suggestions.isEmpty()) { - auto comboBox = new QComboBox(parent); - comboBox->setEditable(true); - comboBox->addItems(suggestions); - comboBox->setCurrentText(manager->value(property).toString()); - mCreatedComboBoxes[property].append(comboBox); - mComboBoxToProperty[comboBox] = property; - - connect(comboBox, &QComboBox::currentTextChanged, - this, &VariantEditorFactory::comboBoxPropertyEditTextChanged); - connect(comboBox, &QObject::destroyed, - this, &VariantEditorFactory::slotEditorDestroyed); - - editor = comboBox; - } - } - - if (!editor) - editor = QtVariantEditorFactory::createEditor(manager, property, parent); - - if (type == QMetaType::QColor || type == VariantPropertyManager::displayObjectRefTypeId() || property->isModified()) { - // Allow resetting color and object reference properties, or allow - // unsetting a class member (todo: resolve conflict...). - auto resetWidget = new ResetWidget(property, editor, parent); - connect(resetWidget, &ResetWidget::resetProperty, - this, &VariantEditorFactory::resetProperty); - editor = resetWidget; - } - - return editor; -} - -void VariantEditorFactory::disconnectPropertyManager(QtVariantPropertyManager *manager) -{ - disconnect(manager, &QtVariantPropertyManager::valueChanged, - this, &VariantEditorFactory::slotPropertyChanged); - disconnect(manager, &QtVariantPropertyManager::attributeChanged, - this, &VariantEditorFactory::slotPropertyAttributeChanged); - QtVariantEditorFactory::disconnectPropertyManager(manager); -} - -void VariantEditorFactory::slotPropertyChanged(QtProperty *property, - const QVariant &value) -{ - if (mCreatedFileEdits.contains(property)) { - for (FileEdit *edit : std::as_const(mCreatedFileEdits)[property]) { - FilePath filePath = value.value(); - edit->setFileUrl(filePath.url); - } - } - else if (mCreatedTilesetEdits.contains(property)) { - for (TilesetParametersEdit *edit : std::as_const(mCreatedTilesetEdits)[property]) - edit->setTilesetDocument(value.value()); - } - else if (mCreatedTextPropertyEdits.contains(property)) { - for (TextPropertyEdit *edit : std::as_const(mCreatedTextPropertyEdits)[property]) - edit->setText(value.toString()); - } - else if (mCreatedComboBoxes.contains(property)) { - for (QComboBox *comboBox : std::as_const(mCreatedComboBoxes)[property]) - comboBox->setCurrentText(value.toString()); - } - else if (mCreatedObjectRefEdits.contains(property)) { - for (ObjectRefEdit *objectRefEdit : std::as_const(mCreatedObjectRefEdits)[property]) - objectRefEdit->setValue(value.value()); - } -} - -void VariantEditorFactory::slotPropertyAttributeChanged(QtProperty *property, - const QString &attribute, - const QVariant &value) -{ - if (mCreatedFileEdits.contains(property)) { - if (attribute == QLatin1String("filter")) { - for (FileEdit *edit : std::as_const(mCreatedFileEdits)[property]) - edit->setFilter(value.toString()); - } else if (attribute == QLatin1String("directory")) { - for (FileEdit *edit : std::as_const(mCreatedFileEdits)[property]) - edit->setIsDirectory(value.toBool()); - } - } - else if (mCreatedComboBoxes.contains(property)) { - if (attribute == QLatin1String("suggestions")) { - for (QComboBox *comboBox: std::as_const(mCreatedComboBoxes)[property]) { - comboBox->clear(); - comboBox->addItems(value.toStringList()); - } - } - } - // changing of "multiline" attribute currently not supported -} - -void VariantEditorFactory::fileEditFileUrlChanged(const QUrl &value) -{ - auto fileEdit = qobject_cast(sender()); - Q_ASSERT(fileEdit); - - if (QtProperty *property = mFileEditToProperty.value(fileEdit)) { - QtVariantPropertyManager *manager = propertyManager(property); - if (!manager) - return; - manager->setValue(property, QVariant::fromValue(FilePath { value })); - } -} - -void VariantEditorFactory::textPropertyEditTextChanged(const QString &value) -{ - auto textPropertyEdit = qobject_cast(sender()); - Q_ASSERT(textPropertyEdit); - - if (QtProperty *property = mTextPropertyEditToProperty.value(textPropertyEdit)) { - QtVariantPropertyManager *manager = propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - } -} - -void VariantEditorFactory::comboBoxPropertyEditTextChanged(const QString &value) -{ - auto comboBox = qobject_cast(sender()); - Q_ASSERT(comboBox); - - if (QtProperty *property = mComboBoxToProperty.value(comboBox)) { - QtVariantPropertyManager *manager = propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - } -} - -void VariantEditorFactory::objectRefEditValueChanged(const DisplayObjectRef &value) -{ - auto objectRefEdit = qobject_cast(sender()); - Q_ASSERT(objectRefEdit); - if (QtProperty *property = mObjectRefEditToProperty.value(objectRefEdit)) { - QtVariantPropertyManager *manager = propertyManager(property); - if (!manager) - return; - manager->setValue(property, QVariant::fromValue(value)); - } -} - -void VariantEditorFactory::slotEditorDestroyed(QObject *object) -{ - // Check if it was an ObjectRefEdit - { - auto objectRefEdit = static_cast(object); - - if (QtProperty *property = mObjectRefEditToProperty.value(objectRefEdit)) { - mObjectRefEditToProperty.remove(objectRefEdit); - mCreatedObjectRefEdits[property].removeAll(objectRefEdit); - if (mCreatedObjectRefEdits[property].isEmpty()) - mCreatedObjectRefEdits.remove(property); - return; - } - } - - // Check if it was a FileEdit - { - auto fileEdit = static_cast(object); - - if (QtProperty *property = mFileEditToProperty.value(fileEdit)) { - mFileEditToProperty.remove(fileEdit); - mCreatedFileEdits[property].removeAll(fileEdit); - if (mCreatedFileEdits[property].isEmpty()) - mCreatedFileEdits.remove(property); - return; - } - } - - // Check if it was a TilesetParametersEdit - { - auto tilesetEdit = static_cast(object); - - if (QtProperty *property = mTilesetEditToProperty.value(tilesetEdit)) { - mTilesetEditToProperty.remove(tilesetEdit); - mCreatedTilesetEdits[property].removeAll(tilesetEdit); - if (mCreatedTilesetEdits[property].isEmpty()) - mCreatedTilesetEdits.remove(property); - return; - } - } - - // Check if it was a TextPropertyEdit - { - auto textPropertyEdit = static_cast(object); - - if (QtProperty *property = mTextPropertyEditToProperty.value(textPropertyEdit)) { - mTextPropertyEditToProperty.remove(textPropertyEdit); - mCreatedTextPropertyEdits[property].removeAll(textPropertyEdit); - if (mCreatedTextPropertyEdits[property].isEmpty()) - mCreatedTextPropertyEdits.remove(property); - return; - } - } - - // Check if it was a QComboBox - { - auto comboBox = static_cast(object); - - if (QtProperty *property = mComboBoxToProperty.value(comboBox)) { - mComboBoxToProperty.remove(comboBox); - mCreatedComboBoxes[property].removeAll(comboBox); - if (mCreatedComboBoxes[property].isEmpty()) - mCreatedComboBoxes.remove(property); - return; - } - } -} - -} // namespace Tiled - -#include "varianteditorfactory.moc" -#include "moc_varianteditorfactory.cpp" diff --git a/src/tiled/varianteditorfactory.h b/src/tiled/varianteditorfactory.h deleted file mode 100644 index 630a83f013..0000000000 --- a/src/tiled/varianteditorfactory.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * varianteditorfactory.h - * Copyright (C) 2006 Trolltech ASA. All rights reserved. (GPLv2) - * Copyright 2013-2021, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once - -#include - -class QComboBox; - -namespace Tiled { - -class DisplayObjectRef; -class FileEdit; -class ObjectRefEdit; -class TextPropertyEdit; -class TilesetParametersEdit; - -/** - * Extension of the QtVariantEditorFactory that adds support for: - * - * - An editor for editing file references - * - An editor with a button for changing tileset parameters - * - An editor for editing references to objects - * - "suggestions" and "multiline" attributes for string values. - */ -class VariantEditorFactory : public QtVariantEditorFactory -{ - Q_OBJECT - -public: - explicit VariantEditorFactory(QObject *parent = nullptr) - : QtVariantEditorFactory(parent) - {} - - ~VariantEditorFactory() override; - -signals: - void resetProperty(QtProperty *property); - -protected: - void connectPropertyManager(QtVariantPropertyManager *manager) override; - QWidget *createEditor(QtVariantPropertyManager *manager, - QtProperty *property, - QWidget *parent) override; - void disconnectPropertyManager(QtVariantPropertyManager *manager) override; - -private: - void slotPropertyChanged(QtProperty *property, const QVariant &value); - void slotPropertyAttributeChanged(QtProperty *property, - const QString &attribute, - const QVariant &value); - void fileEditFileUrlChanged(const QUrl &value); - void textPropertyEditTextChanged(const QString &value); - void comboBoxPropertyEditTextChanged(const QString &value); - void objectRefEditValueChanged(const DisplayObjectRef &value); - void slotEditorDestroyed(QObject *object); - - QMap > mCreatedFileEdits; - QMap mFileEditToProperty; - - QMap > mCreatedTilesetEdits; - QMap mTilesetEditToProperty; - - QMap > mCreatedTextPropertyEdits; - QMap mTextPropertyEditToProperty; - - QMap > mCreatedComboBoxes; - QMap mComboBoxToProperty; - - QMap > mCreatedObjectRefEdits; - QMap mObjectRefEditToProperty; -}; - -} // namespace Tiled diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index 0acec4a301..ab42d22446 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -24,7 +24,6 @@ #include "objectrefedit.h" #include "preferences.h" #include "utils.h" -#include "variantpropertymanager.h" #include #include diff --git a/src/tiled/variantpropertymanager.cpp b/src/tiled/variantpropertymanager.cpp deleted file mode 100644 index d54fd3c4cb..0000000000 --- a/src/tiled/variantpropertymanager.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* - * variantpropertymanager.cpp - * Copyright (C) 2006 Trolltech ASA. All rights reserved. (GPLv2) - * Copyright 2013, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "variantpropertymanager.h" - -#include "documentmanager.h" -#include "mapdocument.h" -#include "mapobject.h" -#include "mapobjectmodel.h" -#include "objectgroup.h" -#include "textpropertyedit.h" -#include "tilesetdocument.h" - -#include - -namespace Tiled { - -class TilesetParametersPropertyType {}; - -} // namespace Tiled - -// Needs to be up here rather than at the bottom of the file to make a -// static_assert in qMetaTypeId work (as of C++11) -Q_DECLARE_METATYPE(Tiled::TilesetParametersPropertyType) - -namespace Tiled { - -MapObject *DisplayObjectRef::object() const -{ - if (!mapDocument || ref.id <= 0) - return nullptr; - return mapDocument->map()->findObjectById(ref.id); -} - - -VariantPropertyManager::VariantPropertyManager(QObject *parent) - : QtVariantPropertyManager(parent) - , mFilterAttribute(QStringLiteral("filter")) - , mDirectoryAttribute(QStringLiteral("directory")) - , mSuggestionsAttribute(QStringLiteral("suggestions")) - , mMultilineAttribute(QStringLiteral("multiline")) - , mImageMissingIcon(QStringLiteral("://images/16/image-missing.png")) -{ - mImageMissingIcon.addPixmap(QPixmap(QStringLiteral("://images/32/image-missing.png"))); - - connect(this, &QtVariantPropertyManager::valueChanged, - this, &VariantPropertyManager::slotValueChanged); - connect(this, &QtAbstractPropertyManager::propertyDestroyed, - this, &VariantPropertyManager::slotPropertyDestroyed); -} - -VariantPropertyManager::~VariantPropertyManager() -{ - // Disconnect early since these functions may be called during destruction - disconnect(this, &QtVariantPropertyManager::valueChanged, - this, &VariantPropertyManager::slotValueChanged); - disconnect(this, &QtAbstractPropertyManager::propertyDestroyed, - this, &VariantPropertyManager::slotPropertyDestroyed); -} - -QVariant VariantPropertyManager::value(const QtProperty *property) const -{ - if (mValues.contains(property)) - return mValues[property]; - if (m_alignValues.contains(property)) - return QVariant::fromValue(m_alignValues.value(property)); - return QtVariantPropertyManager::value(property); -} - -bool VariantPropertyManager::isPropertyTypeSupported(int propertyType) const -{ - if (propertyType == filePathTypeId() - || propertyType == displayObjectRefTypeId() - || propertyType == tilesetParametersTypeId() - || propertyType == alignmentTypeId() - || propertyType == unstyledGroupTypeId()) - return true; - return QtVariantPropertyManager::isPropertyTypeSupported(propertyType); -} - -int VariantPropertyManager::valueType(int propertyType) const -{ - if (propertyType == filePathTypeId()) - return propertyType; - if (propertyType == displayObjectRefTypeId()) - return propertyType; - if (propertyType == tilesetParametersTypeId()) - return qMetaTypeId(); - if (propertyType == alignmentTypeId()) - return propertyType; - if (propertyType == unstyledGroupTypeId()) - return QMetaType::QVariantMap; // allows storing any child values - return QtVariantPropertyManager::valueType(propertyType); -} - -QStringList VariantPropertyManager::attributes(int propertyType) const -{ - if (propertyType == filePathTypeId()) { - return { - mFilterAttribute, - mDirectoryAttribute - }; - } - return QtVariantPropertyManager::attributes(propertyType); -} - -int VariantPropertyManager::attributeType(int propertyType, - const QString &attribute) const -{ - if (propertyType == filePathTypeId()) { - if (attribute == mFilterAttribute) - return QMetaType::QString; - if (attribute == mDirectoryAttribute) - return QMetaType::Bool; - return 0; - } - return QtVariantPropertyManager::attributeType(propertyType, attribute); -} - -QVariant VariantPropertyManager::attributeValue(const QtProperty *property, - const QString &attribute) const -{ - if (mFilePathAttributes.contains(property)) { - if (attribute == mFilterAttribute) - return mFilePathAttributes[property].filter; - if (attribute == mDirectoryAttribute) - return mFilePathAttributes[property].directory; - return QVariant(); - } - if (mStringAttributes.contains(property)) { - if (attribute == mSuggestionsAttribute) - return mStringAttributes[property].suggestions; - if (attribute == mMultilineAttribute) - return mStringAttributes[property].multiline; - } - - return QtVariantPropertyManager::attributeValue(property, attribute); -} - -int VariantPropertyManager::tilesetParametersTypeId() -{ - return qMetaTypeId(); -} - -int VariantPropertyManager::alignmentTypeId() -{ - return qMetaTypeId(); -} - -int VariantPropertyManager::displayObjectRefTypeId() -{ - return qMetaTypeId(); -} - -int VariantPropertyManager::unstyledGroupTypeId() -{ - return qMetaTypeId(); -} - -QString VariantPropertyManager::objectRefLabel(const MapObject &object) -{ - const QString &className = object.effectiveClassName(); - - QString label = tr("%1: ").arg(object.id()); - if (!object.name().isEmpty()) { - label.append(object.name()); - if (!className.isEmpty()) - label.append(tr(" (%1)").arg(className)); - } else if (!className.isEmpty()) - label.append(tr("(%1)").arg(className)); - else - label.append(tr("Unnamed object")); - - return label; -} - -QString VariantPropertyManager::valueText(const QtProperty *property) const -{ - if (mValues.contains(property)) { - QVariant value = mValues[property]; - int typeId = propertyType(property); - - if (typeId == displayObjectRefTypeId()) { - const auto ref = value.value(); - - if (ref.id() == 0) - return tr("Unset"); - - if (auto object = ref.object()) - return objectRefLabel(*object); - - return tr("%1: Object not found").arg(QString::number(ref.id())); - } - - if (typeId == filePathTypeId()) { - FilePath filePath = value.value(); - QString fileName = filePath.url.fileName(); - if (fileName.isEmpty()) { - QString path = filePath.url.toLocalFile(); - if (path.endsWith(QLatin1Char('/'))) - path.chop(1); - fileName = QFileInfo(path).fileName(); - } - return fileName; - } - - if (typeId == tilesetParametersTypeId()) { - if (TilesetDocument *tilesetDocument = value.value()) - return tilesetDocument->tileset()->imageSource().fileName(); - } - - return value.toString(); - } - - if (m_alignValues.contains(const_cast(property))) { - const Qt::Alignment v = m_alignValues.value(const_cast(property)); - return tr("%1, %2").arg(indexHToString(alignToIndexH(v)), - indexVToString(alignToIndexV(v))); - } - - auto stringAttributesIt = mStringAttributes.find(property); - if (stringAttributesIt != mStringAttributes.end()) { - if ((*stringAttributesIt).multiline) - return escapeNewlines(value(property).toString()); - } - - return QtVariantPropertyManager::valueText(property); -} - -QIcon VariantPropertyManager::valueIcon(const QtProperty *property) const -{ - if (mValues.contains(property)) { - const QVariant value = mValues[property]; - const int typeId = propertyType(property); - - if (typeId == displayObjectRefTypeId()) { - const DisplayObjectRef ref = value.value(); - if (auto object = ref.object()) - return ObjectIconManager::instance().iconForObject(*object); - } - - if (typeId == unstyledGroupTypeId()) - return QIcon(); - - QString filePath; - - // TODO: Needs a special icon for remote files - if (typeId == filePathTypeId()) { - const FilePath fp = value.value(); - filePath = fp.url.toLocalFile(); - } - - if (typeId == tilesetParametersTypeId()) { - if (TilesetDocument *tilesetDocument = value.value()) - filePath = tilesetDocument->tileset()->imageSource().toLocalFile(); - } - - // TODO: This assumes the file path is an image reference. It should be - // replaced with a more generic icon. - if (filePath.isEmpty() || !QFile::exists(filePath)) - return QIcon::fromTheme(QLatin1String("image-missing"), mImageMissingIcon); - else - return mIconProvider.icon(QFileInfo(filePath)); - } - - return QtVariantPropertyManager::valueIcon(property); -} - -void VariantPropertyManager::setValue(QtProperty *property, const QVariant &value) -{ - if (mValues.contains(property)) { - QVariant &storedValue = mValues[property]; - if (storedValue == value) - return; - storedValue = value; - emit propertyChanged(property); - emit valueChanged(property, value); - return; - } else if (m_alignValues.contains(property)) { - if (value.userType() != qMetaTypeId() && !value.canConvert(qMetaTypeId())) - return; - - const Qt::Alignment v = value.value(); - const Qt::Alignment val = m_alignValues.value(property); - - if (val == v) - return; - - QtVariantProperty *alignH = variantProperty(m_propertyToAlignH.value(property)); - QtVariantProperty *alignV = variantProperty(m_propertyToAlignV.value(property)); - - if (alignH) - alignH->setValue(alignToIndexH(v)); - if (alignV) - alignV->setValue(alignToIndexV(v)); - - m_alignValues[property] = v; - - emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(v)); - emit propertyChanged(property); - - return; - } - QtVariantPropertyManager::setValue(property, value); -} - -void VariantPropertyManager::setAttribute(QtProperty *property, - const QString &attribute, - const QVariant &val) -{ - if (mFilePathAttributes.contains(property)) { - FilePathAttributes &attributes = mFilePathAttributes[property]; - if (attribute == mFilterAttribute) { - if (val.userType() != QMetaType::QString && !val.canConvert(QMetaType::QString)) - return; - QString filter = val.toString(); - if (attributes.filter == filter) - return; - attributes.filter = filter; - emit attributeChanged(property, attribute, filter); - } else if (attribute == mDirectoryAttribute) { - bool directory = val.toBool(); - if (attributes.directory == directory) - return; - attributes.directory = directory; - emit attributeChanged(property, attribute, directory); - } - return; - } - - if (mStringAttributes.contains(property)) { - StringAttributes &attributes = mStringAttributes[property]; - if (attribute == mSuggestionsAttribute) { - const QStringList suggestions = val.toStringList(); - if (attributes.suggestions == suggestions) - return; - attributes.suggestions = suggestions; - emit attributeChanged(property, attribute, val); - return; - } - if (attribute == mMultilineAttribute) { - attributes.multiline = val.toBool(); - return; - } - } - - QtVariantPropertyManager::setAttribute(property, attribute, val); -} - -void VariantPropertyManager::initializeProperty(QtProperty *property) -{ - const int type = propertyType(property); - if (type == filePathTypeId() - || type == displayObjectRefTypeId() - || type == tilesetParametersTypeId() - || type == unstyledGroupTypeId()) { // for storing hash map - mValues[property] = QVariant(); - if (type == filePathTypeId()) - mFilePathAttributes[property] = FilePathAttributes(); - } else if (type == QMetaType::QString) { - mStringAttributes[property] = StringAttributes(); - } else if (type == alignmentTypeId()) { - const Qt::Alignment align = Qt::AlignLeft | Qt::AlignVCenter; - m_alignValues[property] = align; - - QtVariantProperty *alignH = addProperty(enumTypeId(), tr("Horizontal")); - QStringList namesH; - namesH << indexHToString(0) << indexHToString(1) << indexHToString(2) << indexHToString(3); - alignH->setAttribute(QStringLiteral("enumNames"), namesH); - alignH->setValue(alignToIndexH(align)); - m_propertyToAlignH[property] = alignH; - m_alignHToProperty[alignH] = property; - property->addSubProperty(alignH); - - QtVariantProperty *alignV = addProperty(enumTypeId(), tr("Vertical")); - QStringList namesV; - namesV << indexVToString(0) << indexVToString(1) << indexVToString(2); - alignV->setAttribute(QStringLiteral("enumNames"), namesV); - alignV->setValue(alignToIndexV(align)); - m_propertyToAlignV[property] = alignV; - m_alignVToProperty[alignV] = property; - property->addSubProperty(alignV); - } - - - QtVariantPropertyManager::initializeProperty(property); -} - -void VariantPropertyManager::uninitializeProperty(QtProperty *property) -{ - mValues.remove(property); - mFilePathAttributes.remove(property); - mStringAttributes.remove(property); - m_alignValues.remove(property); - - QtProperty *alignH = m_propertyToAlignH.value(property); - if (alignH) { - delete alignH; - m_alignHToProperty.remove(alignH); - } - QtProperty *alignV = m_propertyToAlignV.value(property); - if (alignV) { - delete alignV; - m_alignVToProperty.remove(alignV); - } - - QtVariantPropertyManager::uninitializeProperty(property); -} - -void VariantPropertyManager::slotValueChanged(QtProperty *property, const QVariant &value) -{ - if (QtProperty *alignProperty = m_alignHToProperty.value(property)) { - const Qt::Alignment v = m_alignValues.value(alignProperty); - const Qt::Alignment newValue = indexHToAlign(value.toInt()) | indexVToAlign(alignToIndexV(v)); - if (v == newValue) - return; - - variantProperty(alignProperty)->setValue(QVariant::fromValue(newValue)); - } else if (QtProperty *alignProperty = m_alignVToProperty.value(property)) { - const Qt::Alignment v = m_alignValues.value(alignProperty); - const Qt::Alignment newValue = indexVToAlign(value.toInt()) | indexHToAlign(alignToIndexH(v)); - if (v == newValue) - return; - - variantProperty(alignProperty)->setValue(QVariant::fromValue(newValue)); - } -} - -void VariantPropertyManager::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *alignProperty = m_alignHToProperty.value(property)) { - m_propertyToAlignH.remove(alignProperty); - m_alignHToProperty.remove(property); - } else if (QtProperty *alignProperty = m_alignVToProperty.value(property)) { - m_propertyToAlignV.remove(alignProperty); - m_alignVToProperty.remove(property); - } -} - -int VariantPropertyManager::alignToIndexH(Qt::Alignment align) const -{ - if (align & Qt::AlignLeft) - return 0; - if (align & Qt::AlignHCenter) - return 1; - if (align & Qt::AlignRight) - return 2; - if (align & Qt::AlignJustify) - return 3; - return 0; -} - -int VariantPropertyManager::alignToIndexV(Qt::Alignment align) const -{ - if (align & Qt::AlignTop) - return 0; - if (align & Qt::AlignVCenter) - return 1; - if (align & Qt::AlignBottom) - return 2; - return 1; -} - -Qt::Alignment VariantPropertyManager::indexHToAlign(int idx) const -{ - switch (idx) { - case 0: return Qt::AlignLeft; - case 1: return Qt::AlignHCenter; - case 2: return Qt::AlignRight; - case 3: return Qt::AlignJustify; - default: break; - } - return Qt::AlignLeft; -} - -Qt::Alignment VariantPropertyManager::indexVToAlign(int idx) const -{ - switch (idx) { - case 0: return Qt::AlignTop; - case 1: return Qt::AlignVCenter; - case 2: return Qt::AlignBottom; - default: break; - } - return Qt::AlignVCenter; -} - -QString VariantPropertyManager::indexHToString(int idx) const -{ - switch (idx) { - case 0: return tr("Left"); - case 1: return tr("Center"); - case 2: return tr("Right"); - case 3: return tr("Justify"); - default: break; - } - return tr("Left"); -} - -QString VariantPropertyManager::indexVToString(int idx) const -{ - switch (idx) { - case 0: return tr("Top"); - case 1: return tr("Center"); - case 2: return tr("Bottom"); - default: break; - } - return tr("Center"); -} - -} // namespace Tiled - -#include "moc_variantpropertymanager.cpp" diff --git a/src/tiled/variantpropertymanager.h b/src/tiled/variantpropertymanager.h deleted file mode 100644 index 7ae5923f3e..0000000000 --- a/src/tiled/variantpropertymanager.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * variantpropertymanager.h - * Copyright (C) 2006 Trolltech ASA. All rights reserved. (GPLv2) - * Copyright 2013, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once - -#include "properties.h" - -#include - -#include - -namespace Tiled { - -class MapDocument; -class MapObject; - -class DisplayObjectRef { -public: - explicit DisplayObjectRef(ObjectRef ref = ObjectRef(), - MapDocument *mapDocument = nullptr) - : ref(ref) - , mapDocument(mapDocument) - {} - - bool operator==(const DisplayObjectRef &o) const - { return ref.id == o.ref.id && mapDocument == o.mapDocument; } - - int id() const { return ref.id; } - MapObject *object() const; - - ObjectRef ref; - MapDocument *mapDocument; -}; - -class UnstyledGroup {}; - -/** - * Extension of the QtVariantPropertyManager that adds support for various - * additional types and attributes. - */ -class VariantPropertyManager : public QtVariantPropertyManager -{ - Q_OBJECT - -public: - explicit VariantPropertyManager(QObject *parent = nullptr); - ~VariantPropertyManager() override; - - QVariant value(const QtProperty *property) const override; - int valueType(int propertyType) const override; - bool isPropertyTypeSupported(int propertyType) const override; - - QStringList attributes(int propertyType) const override; - int attributeType(int propertyType, const QString &attribute) const override; - QVariant attributeValue(const QtProperty *property, - const QString &attribute) const override; - - static int tilesetParametersTypeId(); - static int alignmentTypeId(); - static int displayObjectRefTypeId(); - static int unstyledGroupTypeId(); - -public slots: - void setValue(QtProperty *property, const QVariant &val) override; - void setAttribute(QtProperty *property, - const QString &attribute, - const QVariant &value) override; - -protected: - QString valueText(const QtProperty *property) const override; - QIcon valueIcon(const QtProperty *property) const override; - void initializeProperty(QtProperty *property) override; - void uninitializeProperty(QtProperty *property) override; - -private: - void slotValueChanged(QtProperty *property, const QVariant &value); - void slotPropertyDestroyed(QtProperty *property); - - static QString objectRefLabel(const MapObject &object); - - QMap mValues; - - struct FilePathAttributes { - QString filter; - bool directory = false; - }; - QMap mFilePathAttributes; - - struct StringAttributes { - QStringList suggestions; - bool multiline = false; - }; - QMap mStringAttributes; - - int alignToIndexH(Qt::Alignment align) const; - int alignToIndexV(Qt::Alignment align) const; - Qt::Alignment indexHToAlign(int idx) const; - Qt::Alignment indexVToAlign(int idx) const; - QString indexHToString(int idx) const; - QString indexVToString(int idx) const; - QMap m_alignValues; - using PropertyToPropertyMap = QMap; - PropertyToPropertyMap m_propertyToAlignH; - PropertyToPropertyMap m_propertyToAlignV; - PropertyToPropertyMap m_alignHToProperty; - PropertyToPropertyMap m_alignVToProperty; - - const QString mFilterAttribute; - const QString mDirectoryAttribute; - const QString mSuggestionsAttribute; - const QString mMultilineAttribute; - QIcon mImageMissingIcon; - QFileIconProvider mIconProvider; -}; - -} // namespace Tiled - -Q_DECLARE_METATYPE(Tiled::DisplayObjectRef) -Q_DECLARE_METATYPE(Tiled::UnstyledGroup) diff --git a/tiled.qbs b/tiled.qbs index fb54d9ba13..7787903ee5 100644 --- a/tiled.qbs +++ b/tiled.qbs @@ -29,7 +29,6 @@ Project { "src/libtiled", "src/libtiledquick", "src/plugins", - "src/qtpropertybrowser", "src/qtsingleapplication", "src/terraingenerator", "src/tiled/libtilededitor.qbs", From d0d1ff54357917c898456adbbdefdc103f40f1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 8 Nov 2024 21:07:36 +0100 Subject: [PATCH 62/78] Removed the QtPropertyBrowser solution The QtPropertyBrowser solution has served us well for over 10 years. Now it has been replaced by a new widget-based solution with a more convenient API. --- src/qtpropertybrowser/README.TXT | 93 - src/qtpropertybrowser/common.pri | 5 - src/qtpropertybrowser/qtpropertybrowser.qbs | 46 - .../src/QtAbstractEditorFactoryBase | 1 - .../src/QtAbstractPropertyBrowser | 1 - .../src/QtAbstractPropertyManager | 1 - .../src/QtBoolPropertyManager | 1 - src/qtpropertybrowser/src/QtBrowserItem | 1 - .../src/QtButtonPropertyBrowser | 1 - src/qtpropertybrowser/src/QtCharEditorFactory | 1 - .../src/QtCharPropertyManager | 1 - src/qtpropertybrowser/src/QtCheckBoxFactory | 1 - .../src/QtColorEditorFactory | 1 - .../src/QtColorPropertyManager | 1 - .../src/QtCursorEditorFactory | 1 - .../src/QtCursorPropertyManager | 1 - src/qtpropertybrowser/src/QtDateEditFactory | 1 - .../src/QtDatePropertyManager | 1 - .../src/QtDateTimeEditFactory | 1 - .../src/QtDateTimePropertyManager | 1 - .../src/QtDoublePropertyManager | 1 - .../src/QtDoubleSpinBoxFactory | 1 - src/qtpropertybrowser/src/QtEnumEditorFactory | 1 - .../src/QtEnumPropertyManager | 1 - .../src/QtFlagPropertyManager | 1 - src/qtpropertybrowser/src/QtFontEditorFactory | 1 - .../src/QtFontPropertyManager | 1 - .../src/QtGroupBoxPropertyBrowser | 1 - .../src/QtGroupPropertyManager | 1 - .../src/QtIntPropertyManager | 1 - .../src/QtKeySequenceEditorFactory | 1 - .../src/QtKeySequencePropertyManager | 1 - src/qtpropertybrowser/src/QtLineEditFactory | 1 - .../src/QtLocalePropertyManager | 1 - .../src/QtPointFPropertyManager | 1 - .../src/QtPointPropertyManager | 1 - src/qtpropertybrowser/src/QtProperty | 1 - .../src/QtRectFPropertyManager | 1 - .../src/QtRectPropertyManager | 1 - src/qtpropertybrowser/src/QtScrollBarFactory | 1 - .../src/QtSizeFPropertyManager | 1 - .../src/QtSizePolicyPropertyManager | 1 - .../src/QtSizePropertyManager | 1 - src/qtpropertybrowser/src/QtSliderFactory | 1 - src/qtpropertybrowser/src/QtSpinBoxFactory | 1 - .../src/QtStringPropertyManager | 1 - src/qtpropertybrowser/src/QtTimeEditFactory | 1 - .../src/QtTimePropertyManager | 1 - .../src/QtTreePropertyBrowser | 1 - .../src/QtVariantEditorFactory | 1 - src/qtpropertybrowser/src/QtVariantProperty | 1 - .../src/QtVariantPropertyManager | 1 - .../src/images/cursor-arrow.png | Bin 171 -> 0 bytes .../src/images/cursor-busy.png | Bin 201 -> 0 bytes .../src/images/cursor-closedhand.png | Bin 147 -> 0 bytes .../src/images/cursor-cross.png | Bin 130 -> 0 bytes .../src/images/cursor-forbidden.png | Bin 199 -> 0 bytes .../src/images/cursor-hand.png | Bin 159 -> 0 bytes .../src/images/cursor-hsplit.png | Bin 155 -> 0 bytes .../src/images/cursor-ibeam.png | Bin 124 -> 0 bytes .../src/images/cursor-openhand.png | Bin 160 -> 0 bytes .../src/images/cursor-sizeall.png | Bin 174 -> 0 bytes .../src/images/cursor-sizeb.png | Bin 161 -> 0 bytes .../src/images/cursor-sizef.png | Bin 161 -> 0 bytes .../src/images/cursor-sizeh.png | Bin 145 -> 0 bytes .../src/images/cursor-sizev.png | Bin 141 -> 0 bytes .../src/images/cursor-uparrow.png | Bin 132 -> 0 bytes .../src/images/cursor-vsplit.png | Bin 161 -> 0 bytes .../src/images/cursor-wait.png | Bin 172 -> 0 bytes .../src/images/cursor-whatsthis.png | Bin 191 -> 0 bytes .../src/qtbuttonpropertybrowser.cpp | 627 -- .../src/qtbuttonpropertybrowser.h | 89 - src/qtpropertybrowser/src/qteditorfactory.cpp | 2734 -------- src/qtpropertybrowser/src/qteditorfactory.h | 406 -- .../src/qtgroupboxpropertybrowser.cpp | 534 -- .../src/qtgroupboxpropertybrowser.h | 80 - .../src/qtpropertybrowser.cpp | 2079 ------ src/qtpropertybrowser/src/qtpropertybrowser.h | 339 - .../src/qtpropertybrowser.pri | 31 - .../src/qtpropertybrowser.qrc | 23 - .../src/qtpropertybrowserutils.cpp | 331 - .../src/qtpropertybrowserutils_p.h | 136 - .../src/qtpropertymanager.cpp | 5995 ----------------- src/qtpropertybrowser/src/qtpropertymanager.h | 714 -- .../src/qttreepropertybrowser.cpp | 1200 ---- .../src/qttreepropertybrowser.h | 150 - .../src/qtvariantproperty.cpp | 2291 ------- src/qtpropertybrowser/src/qtvariantproperty.h | 188 - 88 files changed, 18140 deletions(-) delete mode 100644 src/qtpropertybrowser/README.TXT delete mode 100644 src/qtpropertybrowser/common.pri delete mode 100644 src/qtpropertybrowser/qtpropertybrowser.qbs delete mode 100644 src/qtpropertybrowser/src/QtAbstractEditorFactoryBase delete mode 100644 src/qtpropertybrowser/src/QtAbstractPropertyBrowser delete mode 100644 src/qtpropertybrowser/src/QtAbstractPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtBoolPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtBrowserItem delete mode 100644 src/qtpropertybrowser/src/QtButtonPropertyBrowser delete mode 100644 src/qtpropertybrowser/src/QtCharEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtCharPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtCheckBoxFactory delete mode 100644 src/qtpropertybrowser/src/QtColorEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtColorPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtCursorEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtCursorPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtDateEditFactory delete mode 100644 src/qtpropertybrowser/src/QtDatePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtDateTimeEditFactory delete mode 100644 src/qtpropertybrowser/src/QtDateTimePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtDoublePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtDoubleSpinBoxFactory delete mode 100644 src/qtpropertybrowser/src/QtEnumEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtEnumPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtFlagPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtFontEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtFontPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtGroupBoxPropertyBrowser delete mode 100644 src/qtpropertybrowser/src/QtGroupPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtIntPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtKeySequenceEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtKeySequencePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtLineEditFactory delete mode 100644 src/qtpropertybrowser/src/QtLocalePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtPointFPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtPointPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtProperty delete mode 100644 src/qtpropertybrowser/src/QtRectFPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtRectPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtScrollBarFactory delete mode 100644 src/qtpropertybrowser/src/QtSizeFPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtSizePolicyPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtSizePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtSliderFactory delete mode 100644 src/qtpropertybrowser/src/QtSpinBoxFactory delete mode 100644 src/qtpropertybrowser/src/QtStringPropertyManager delete mode 100644 src/qtpropertybrowser/src/QtTimeEditFactory delete mode 100644 src/qtpropertybrowser/src/QtTimePropertyManager delete mode 100644 src/qtpropertybrowser/src/QtTreePropertyBrowser delete mode 100644 src/qtpropertybrowser/src/QtVariantEditorFactory delete mode 100644 src/qtpropertybrowser/src/QtVariantProperty delete mode 100644 src/qtpropertybrowser/src/QtVariantPropertyManager delete mode 100644 src/qtpropertybrowser/src/images/cursor-arrow.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-busy.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-closedhand.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-cross.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-forbidden.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-hand.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-hsplit.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-ibeam.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-openhand.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-sizeall.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-sizeb.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-sizef.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-sizeh.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-sizev.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-uparrow.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-vsplit.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-wait.png delete mode 100644 src/qtpropertybrowser/src/images/cursor-whatsthis.png delete mode 100644 src/qtpropertybrowser/src/qtbuttonpropertybrowser.cpp delete mode 100644 src/qtpropertybrowser/src/qtbuttonpropertybrowser.h delete mode 100644 src/qtpropertybrowser/src/qteditorfactory.cpp delete mode 100644 src/qtpropertybrowser/src/qteditorfactory.h delete mode 100644 src/qtpropertybrowser/src/qtgroupboxpropertybrowser.cpp delete mode 100644 src/qtpropertybrowser/src/qtgroupboxpropertybrowser.h delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowser.cpp delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowser.h delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowser.pri delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowser.qrc delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowserutils.cpp delete mode 100644 src/qtpropertybrowser/src/qtpropertybrowserutils_p.h delete mode 100644 src/qtpropertybrowser/src/qtpropertymanager.cpp delete mode 100644 src/qtpropertybrowser/src/qtpropertymanager.h delete mode 100644 src/qtpropertybrowser/src/qttreepropertybrowser.cpp delete mode 100644 src/qtpropertybrowser/src/qttreepropertybrowser.h delete mode 100644 src/qtpropertybrowser/src/qtvariantproperty.cpp delete mode 100644 src/qtpropertybrowser/src/qtvariantproperty.h diff --git a/src/qtpropertybrowser/README.TXT b/src/qtpropertybrowser/README.TXT deleted file mode 100644 index 148c47b4ba..0000000000 --- a/src/qtpropertybrowser/README.TXT +++ /dev/null @@ -1,93 +0,0 @@ -Qt Solutions Component: Property Browser - -A property browser framework enabling the user to edit a set of -properties. - -The framework provides a browser widget that displays the given -properties with labels and corresponding editing widgets (e.g. -line edits or comboboxes). The various types of editing widgets -are provided by the framework's editor factories: For each -property type, the framework provides a property manager (e.g. -QtIntPropertyManager and QtStringPropertyManager) which can be -associated with the preferred editor factory (e.g. -QtSpinBoxFactory and QtLineEditFactory). The framework also -provides a variant based property type with corresponding variant -manager and factory. Finally, the framework provides three -ready-made implementations of the browser widget: -QtTreePropertyBrowser, QtButtonPropertyBrowser and -QtGroupBoxPropertyBrowser. - -Version history: - -2.1: - QtTreePropertyBrowser - tooltip of property applied to - first column, while second column shows the value text of property - in its tooltip - - QtAbstractPropertyManager - initializeProperty() and - uninitializeProperty() without const modifier now - - QtTreePropertyBrowser and QtGroupBoxPropertyBrowser - internal - margin set to 0 - - QtProperty - setEnabled() and isEnabled() methods added - - QtTreePropertyBrowser - "rootIsDecorated", "indentation" and - "headerVisible" properties added - - QtProperty - hasValue() method added, useful for group - properties - -2.2: - FocusOut event now filtered out in case of - Qt::ActiveWindowFocusReason reason. In that case editor is not - closed when its sub dialog is executed - - Removed bug in color icon generation - - Decimals attribute added to "double" property type - - PointF, SizeF and RectF types supported - - Proper translation calls for tree property browser - - QtProperty - ensure inserted subproperty is different from - "this" property - - QtBrowserItem class introduced, useful for identifying browser's - gui elements - - Possibility to control expanded state of QtTreePropertyBrowser's - items from code - - QtTreePropertyBrowser - "resizeMode" and "splitterPosition" - properties added - - QtGroupBoxPropertyBrowser - fixed crash in case of deleting the - editor factory and then deleting the manager - - "Decoration" example added - it shows how to add new - responsibilities to the existing managers and editor factories - -2.3: - Various bugfixes and improvements - - QtProperty - setModified() and isModified() methods added - - QtTreePropertyBrowser - disabling an item closes its editor - - KeySequence, Char, Locale and Cursor types supported - - Support for icons in enum type added - - Kerning subproperty exposed in Font type - - New property browser class added - QtButtonPropertyBrowser with - drop down button as a grouping element - -2.4: - Fixed memory leak of QtProperty - - QtTreePropertyBrowser - group items are rendered better - - QtTreePropertyBrowser - propertiesWithoutValueMarked and - alternatingRowColors features added - - QtTreePropertyBrowser - possibility of coloring properties added - - QtTreePropertyBrowser - keyboard navigation improved - - New factories providing popup dialogs added: - QtColorEditorFactory and QtFontEditorFactory - - Single step attribute added to: QtIntPropertyManager and - QtDoublePropertyManager - -2.5: - "Object Controller" example added. It implements a similar - widget to the property editor in QDesigner - - Compile with QT_NO_CURSOR - - Expand root item with single click on the '+' icon - - QtRectPropertyManager and QtRectFPropertyManager - by default - constraint is null rect meaning no constraint is applied - -2.6: - QtGroupPropertyBrowser - don't force the layout to show the - whole labels' contents for read only properties, show tooltips for - them in addition. - - QtTreePropertyBrowser - fixed painting of the editor for color - property type when style sheet is used (QTSOLBUG-64). - - Make it possible to change the style of the checkboxes with a - stylesheet (QTSOLBUG-61). - - Change the minimum size of a combobox so that it can show at - least one character and an icon. - - Make it possible to properly style custom embedded editors (e.g. - the color editor provided with the solution). - diff --git a/src/qtpropertybrowser/common.pri b/src/qtpropertybrowser/common.pri deleted file mode 100644 index d6178c6645..0000000000 --- a/src/qtpropertybrowser/common.pri +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE += fakelib -QTPROPERTYBROWSER_LIBNAME = $$qtLibraryTarget(QtSolutions_PropertyBrowser-head) -TEMPLATE -= fakelib -QTPROPERTYBROWSER_LIBDIR = $$PWD/lib -unix:qtpropertybrowser-uselib:!qtpropertybrowser-buildlib:QMAKE_RPATHDIR += $$QTPROPERTYBROWSER_LIBDIR diff --git a/src/qtpropertybrowser/qtpropertybrowser.qbs b/src/qtpropertybrowser/qtpropertybrowser.qbs deleted file mode 100644 index 53bad9bab8..0000000000 --- a/src/qtpropertybrowser/qtpropertybrowser.qbs +++ /dev/null @@ -1,46 +0,0 @@ -StaticLibrary { - name: "qtpropertybrowser" - - Depends { name: "cpp" } - Depends { name: "Qt"; submodules: ["widgets"] } - - cpp.includePaths: ["src"] - cpp.cxxLanguageVersion: "c++17" - cpp.cxxFlags: { - var flags = base; - if (qbs.toolchain.contains("msvc")) { - if (Qt.core.versionMajor >= 6 && Qt.core.versionMinor >= 3) - flags.push("/permissive-"); - } else if (qbs.toolchain.contains("mingw")) { - // needed to work around "too many sections" issue in qteditorfactory.cpp - if (Qt.core.versionMajor >= 6 && Qt.core.versionMinor >= 2) - flags.push("-Wa,-mbig-obj"); - } - return flags; - } - cpp.visibility: "minimal" - - files: [ - "src/qtbuttonpropertybrowser.cpp", - "src/qtbuttonpropertybrowser.h", - "src/qteditorfactory.cpp", - "src/qteditorfactory.h", - "src/qtgroupboxpropertybrowser.cpp", - "src/qtgroupboxpropertybrowser.h", - "src/qtpropertybrowser.cpp", - "src/qtpropertybrowser.h", - "src/qtpropertybrowserutils.cpp", - "src/qtpropertybrowserutils_p.h", - "src/qtpropertymanager.cpp", - "src/qtpropertymanager.h", - "src/qttreepropertybrowser.cpp", - "src/qttreepropertybrowser.h", - "src/qtvariantproperty.cpp", - "src/qtvariantproperty.h", - ] - - Export { - Depends { name: "cpp" } - cpp.includePaths: "src" - } -} diff --git a/src/qtpropertybrowser/src/QtAbstractEditorFactoryBase b/src/qtpropertybrowser/src/QtAbstractEditorFactoryBase deleted file mode 100644 index ab4e7104a4..0000000000 --- a/src/qtpropertybrowser/src/QtAbstractEditorFactoryBase +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtAbstractPropertyBrowser b/src/qtpropertybrowser/src/QtAbstractPropertyBrowser deleted file mode 100644 index ab4e7104a4..0000000000 --- a/src/qtpropertybrowser/src/QtAbstractPropertyBrowser +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtAbstractPropertyManager b/src/qtpropertybrowser/src/QtAbstractPropertyManager deleted file mode 100644 index ab4e7104a4..0000000000 --- a/src/qtpropertybrowser/src/QtAbstractPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtBoolPropertyManager b/src/qtpropertybrowser/src/QtBoolPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtBoolPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtBrowserItem b/src/qtpropertybrowser/src/QtBrowserItem deleted file mode 100644 index ab4e7104a4..0000000000 --- a/src/qtpropertybrowser/src/QtBrowserItem +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtButtonPropertyBrowser b/src/qtpropertybrowser/src/QtButtonPropertyBrowser deleted file mode 100644 index 56e089704a..0000000000 --- a/src/qtpropertybrowser/src/QtButtonPropertyBrowser +++ /dev/null @@ -1 +0,0 @@ -#include "qtbuttonpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtCharEditorFactory b/src/qtpropertybrowser/src/QtCharEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtCharEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtCharPropertyManager b/src/qtpropertybrowser/src/QtCharPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtCharPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtCheckBoxFactory b/src/qtpropertybrowser/src/QtCheckBoxFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtCheckBoxFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtColorEditorFactory b/src/qtpropertybrowser/src/QtColorEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtColorEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtColorPropertyManager b/src/qtpropertybrowser/src/QtColorPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtColorPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtCursorEditorFactory b/src/qtpropertybrowser/src/QtCursorEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtCursorEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtCursorPropertyManager b/src/qtpropertybrowser/src/QtCursorPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtCursorPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtDateEditFactory b/src/qtpropertybrowser/src/QtDateEditFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtDateEditFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtDatePropertyManager b/src/qtpropertybrowser/src/QtDatePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtDatePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtDateTimeEditFactory b/src/qtpropertybrowser/src/QtDateTimeEditFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtDateTimeEditFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtDateTimePropertyManager b/src/qtpropertybrowser/src/QtDateTimePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtDateTimePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtDoublePropertyManager b/src/qtpropertybrowser/src/QtDoublePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtDoublePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtDoubleSpinBoxFactory b/src/qtpropertybrowser/src/QtDoubleSpinBoxFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtDoubleSpinBoxFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtEnumEditorFactory b/src/qtpropertybrowser/src/QtEnumEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtEnumEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtEnumPropertyManager b/src/qtpropertybrowser/src/QtEnumPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtEnumPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtFlagPropertyManager b/src/qtpropertybrowser/src/QtFlagPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtFlagPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtFontEditorFactory b/src/qtpropertybrowser/src/QtFontEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtFontEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtFontPropertyManager b/src/qtpropertybrowser/src/QtFontPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtFontPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtGroupBoxPropertyBrowser b/src/qtpropertybrowser/src/QtGroupBoxPropertyBrowser deleted file mode 100644 index 27964c0803..0000000000 --- a/src/qtpropertybrowser/src/QtGroupBoxPropertyBrowser +++ /dev/null @@ -1 +0,0 @@ -#include "qtgroupboxpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtGroupPropertyManager b/src/qtpropertybrowser/src/QtGroupPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtGroupPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtIntPropertyManager b/src/qtpropertybrowser/src/QtIntPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtIntPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtKeySequenceEditorFactory b/src/qtpropertybrowser/src/QtKeySequenceEditorFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtKeySequenceEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtKeySequencePropertyManager b/src/qtpropertybrowser/src/QtKeySequencePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtKeySequencePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtLineEditFactory b/src/qtpropertybrowser/src/QtLineEditFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtLineEditFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtLocalePropertyManager b/src/qtpropertybrowser/src/QtLocalePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtLocalePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtPointFPropertyManager b/src/qtpropertybrowser/src/QtPointFPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtPointFPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtPointPropertyManager b/src/qtpropertybrowser/src/QtPointPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtPointPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtProperty b/src/qtpropertybrowser/src/QtProperty deleted file mode 100644 index ab4e7104a4..0000000000 --- a/src/qtpropertybrowser/src/QtProperty +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtRectFPropertyManager b/src/qtpropertybrowser/src/QtRectFPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtRectFPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtRectPropertyManager b/src/qtpropertybrowser/src/QtRectPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtRectPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtScrollBarFactory b/src/qtpropertybrowser/src/QtScrollBarFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtScrollBarFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtSizeFPropertyManager b/src/qtpropertybrowser/src/QtSizeFPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtSizeFPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtSizePolicyPropertyManager b/src/qtpropertybrowser/src/QtSizePolicyPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtSizePolicyPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtSizePropertyManager b/src/qtpropertybrowser/src/QtSizePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtSizePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtSliderFactory b/src/qtpropertybrowser/src/QtSliderFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtSliderFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtSpinBoxFactory b/src/qtpropertybrowser/src/QtSpinBoxFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtSpinBoxFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtStringPropertyManager b/src/qtpropertybrowser/src/QtStringPropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtStringPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtTimeEditFactory b/src/qtpropertybrowser/src/QtTimeEditFactory deleted file mode 100644 index 75f35adabc..0000000000 --- a/src/qtpropertybrowser/src/QtTimeEditFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qteditorfactory.h" diff --git a/src/qtpropertybrowser/src/QtTimePropertyManager b/src/qtpropertybrowser/src/QtTimePropertyManager deleted file mode 100644 index 1842e431d3..0000000000 --- a/src/qtpropertybrowser/src/QtTimePropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtpropertymanager.h" diff --git a/src/qtpropertybrowser/src/QtTreePropertyBrowser b/src/qtpropertybrowser/src/QtTreePropertyBrowser deleted file mode 100644 index aab106c753..0000000000 --- a/src/qtpropertybrowser/src/QtTreePropertyBrowser +++ /dev/null @@ -1 +0,0 @@ -#include "qttreepropertybrowser.h" diff --git a/src/qtpropertybrowser/src/QtVariantEditorFactory b/src/qtpropertybrowser/src/QtVariantEditorFactory deleted file mode 100644 index 8118190d50..0000000000 --- a/src/qtpropertybrowser/src/QtVariantEditorFactory +++ /dev/null @@ -1 +0,0 @@ -#include "qtvariantproperty.h" diff --git a/src/qtpropertybrowser/src/QtVariantProperty b/src/qtpropertybrowser/src/QtVariantProperty deleted file mode 100644 index 8118190d50..0000000000 --- a/src/qtpropertybrowser/src/QtVariantProperty +++ /dev/null @@ -1 +0,0 @@ -#include "qtvariantproperty.h" diff --git a/src/qtpropertybrowser/src/QtVariantPropertyManager b/src/qtpropertybrowser/src/QtVariantPropertyManager deleted file mode 100644 index 8118190d50..0000000000 --- a/src/qtpropertybrowser/src/QtVariantPropertyManager +++ /dev/null @@ -1 +0,0 @@ -#include "qtvariantproperty.h" diff --git a/src/qtpropertybrowser/src/images/cursor-arrow.png b/src/qtpropertybrowser/src/images/cursor-arrow.png deleted file mode 100644 index a69ef4eb6158503c7c67c916aea86e65fdc81f72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2@N{tusfe>Zw~@EOfQR+sk_Oou&-fjcEVpj<&$!X3+c~E>J1*JV{%`IM zZgwlFFx}G*?uS>UO1?P#t>x;u4TbmfTg6u$kN3O$qTon=;uum9xAyEt-c|zv=78lpdFvQ2-{H`#GwYbpmwD+D`;ru|4Sly6 z!u|DfH10id;EhPQ?!aC0$4imznq|kzoDQ|k!q4v~%5Ey$Ds|~#XU>6Rfw7|8GuZ2t ub|G diff --git a/src/qtpropertybrowser/src/images/cursor-closedhand.png b/src/qtpropertybrowser/src/images/cursor-closedhand.png deleted file mode 100644 index b78dd1dac5a827fa698f1993718f22c282019505..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?iy0WWJ3*My{N(AiKtWee z7sn8d^J_1j=3-FbaJl&N|NMQrDg{-+lDl3uEOL6iyxxIhJsV5IbqBU04WsP|EmPEI sG+%tqc!TF=!@MiLH_pW!e7ccq3Qxekuk)up2O7xW>FVdQ&MBb@057mJumAu6 diff --git a/src/qtpropertybrowser/src/images/cursor-cross.png b/src/qtpropertybrowser/src/images/cursor-cross.png deleted file mode 100644 index fe38e744805f61abefcc555e07d399725c3fd7e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz=_jGX#;h346aDZ1vLF6&ZrWY3kt_3XM>{%UFuy$6%gUPvacWfgV8N9#n V_FPj?S`E~~;OXk;vd$@?2>`R9Dxv@Y diff --git a/src/qtpropertybrowser/src/images/cursor-forbidden.png b/src/qtpropertybrowser/src/images/cursor-forbidden.png deleted file mode 100644 index 2b08c4e2a3cafc992459a8f484e6c8e6c3e74857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMr#O@pN$vsfa5*w~^P`kb@;)w}Ny_&ztDY@kvlRIOU`3qAsV; zTo+`Y{*UQrTXjS>sKxPHd$OYcrG25=M&E9}?0tT1Yy7oqDMB$hU$uYoMqOpDnka4~ qygb6(Z%*F(qx}EZO#W7L6_Nk?#pi+cGI+ZBxvX&}>iqs75rOW+zd6_S+ yS|~6}xHpZ9x7T5rLt)D22WQ%mvv4FO#t=)GwlEX diff --git a/src/qtpropertybrowser/src/images/cursor-ibeam.png b/src/qtpropertybrowser/src/images/cursor-ibeam.png deleted file mode 100644 index 097fc5fa7287da71ffd907b3a11adbda4516aca6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqgTe~DWM4f DkdQT7 diff --git a/src/qtpropertybrowser/src/images/cursor-sizeall.png b/src/qtpropertybrowser/src/images/cursor-sizeall.png deleted file mode 100644 index 69f13eb347a6c299e06844729a14f657b282fe8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq(Z!J(8lD*)YR5__{zaA2hTRnmr4+Dcjj*u+}&-pB%i42~uelF{r5}E+x CJu-a& diff --git a/src/qtpropertybrowser/src/images/cursor-sizef.png b/src/qtpropertybrowser/src/images/cursor-sizef.png deleted file mode 100644 index 3b127a05d34b48a2f4f9b9cc77b681c6b43afcdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqD?iBlbDB7>)^pUXO@geCxo C3NS_h diff --git a/src/qtpropertybrowser/src/images/cursor-sizeh.png b/src/qtpropertybrowser/src/images/cursor-sizeh.png deleted file mode 100644 index a9f40cbc3d77c566c11c32c0631b4f94f44dd441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq&V7RQzr9bPir8&?922WQ%mvv4FO#sHOC(Zx> diff --git a/src/qtpropertybrowser/src/images/cursor-sizev.png b/src/qtpropertybrowser/src/images/cursor-sizev.png deleted file mode 100644 index 1edbab27a5b05555aaf515931f69ad6bf7e417f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqM`U1 diff --git a/src/qtpropertybrowser/src/images/cursor-wait.png b/src/qtpropertybrowser/src/images/cursor-wait.png deleted file mode 100644 index 69056c479e9b2f009e366dfd71999a7c74f97620..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2^mK6y;h346;J_ZVI&3Y=<%J7po~YS&;ev>WRgqc?pLgqp;N^OZ7kj)9 zt2OYRn)8&w&_Fn;AZ-e>OWiG&j$ap41wJoZG>h5TTvwNMesusN!}2q-U)D!*O8^aL N@O1TaS?83{1OR$JJ0<`C diff --git a/src/qtpropertybrowser/src/images/cursor-whatsthis.png b/src/qtpropertybrowser/src/images/cursor-whatsthis.png deleted file mode 100644 index b47601c3780eec780fdae43bab7481bbfebdddae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*Z*JuL59RLGuTpf8VsdEq+OT~Tw87C;38om{D5bL fNHFu^21W*zFY -#include -#include -#include -#include -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtButtonPropertyBrowserPrivate -{ - QtButtonPropertyBrowser *q_ptr; - Q_DECLARE_PUBLIC(QtButtonPropertyBrowser) -public: - - void init(QWidget *parent); - - void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); - void propertyRemoved(QtBrowserItem *index); - void propertyChanged(QtBrowserItem *index); - QWidget *createEditor(QtProperty *property, QWidget *parent) const - { return q_ptr->createEditor(property, parent); } - - void slotEditorDestroyed(); - void slotUpdate(); - void slotToggled(bool checked); - - struct WidgetItem - { - WidgetItem() : widget(0), label(0), widgetLabel(0), - button(0), container(0), layout(0), /*line(0), */parent(0), expanded(false) { } - QWidget *widget; // can be null - QLabel *label; // main label with property name - QLabel *widgetLabel; // label substitute showing the current value if there is no widget - QToolButton *button; // expandable button for items with children - QWidget *container; // container which is expanded when the button is clicked - QGridLayout *layout; // layout in container - WidgetItem *parent; - QList children; - bool expanded; - }; -private: - void updateLater(); - void updateItem(WidgetItem *item); - void insertRow(QGridLayout *layout, int row) const; - void removeRow(QGridLayout *layout, int row) const; - int gridRow(WidgetItem *item) const; - int gridSpan(WidgetItem *item) const; - void setExpanded(WidgetItem *item, bool expanded); - QToolButton *createButton(QWidget *panret = 0) const; - - QMap m_indexToItem; - QMap m_itemToIndex; - QMap m_widgetToItem; - QMap m_buttonToItem; - QGridLayout *m_mainLayout; - QList m_children; - QList m_recreateQueue; -}; - -QToolButton *QtButtonPropertyBrowserPrivate::createButton(QWidget *parent) const -{ - QToolButton *button = new QToolButton(parent); - button->setCheckable(true); - button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); - button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - button->setArrowType(Qt::DownArrow); - button->setIconSize(QSize(3, 16)); - /* - QIcon icon; - icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowDown), QIcon::Normal, QIcon::Off); - icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowUp), QIcon::Normal, QIcon::On); - button->setIcon(icon); - */ - return button; -} - -int QtButtonPropertyBrowserPrivate::gridRow(WidgetItem *item) const -{ - QList siblings; - if (item->parent) - siblings = item->parent->children; - else - siblings = m_children; - - int row = 0; - QListIterator it(siblings); - while (it.hasNext()) { - WidgetItem *sibling = it.next(); - if (sibling == item) - return row; - row += gridSpan(sibling); - } - return -1; -} - -int QtButtonPropertyBrowserPrivate::gridSpan(WidgetItem *item) const -{ - if (item->container && item->expanded) - return 2; - return 1; -} - -void QtButtonPropertyBrowserPrivate::init(QWidget *parent) -{ - m_mainLayout = new QGridLayout(); - parent->setLayout(m_mainLayout); - QLayoutItem *item = new QSpacerItem(0, 0, - QSizePolicy::Fixed, QSizePolicy::Expanding); - m_mainLayout->addItem(item, 0, 0); -} - -void QtButtonPropertyBrowserPrivate::slotEditorDestroyed() -{ - QWidget *editor = qobject_cast(q_ptr->sender()); - if (!editor) - return; - if (!m_widgetToItem.contains(editor)) - return; - m_widgetToItem[editor]->widget = 0; - m_widgetToItem.remove(editor); -} - -void QtButtonPropertyBrowserPrivate::slotUpdate() -{ - QListIterator itItem(m_recreateQueue); - while (itItem.hasNext()) { - WidgetItem *item = itItem.next(); - - WidgetItem *parent = item->parent; - QWidget *w = 0; - QGridLayout *l = 0; - const int oldRow = gridRow(item); - if (parent) { - w = parent->container; - l = parent->layout; - } else { - w = q_ptr; - l = m_mainLayout; - } - - int span = 1; - if (!item->widget && !item->widgetLabel) - span = 2; - item->label = new QLabel(w); - item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - l->addWidget(item->label, oldRow, 0, 1, span); - - updateItem(item); - } - m_recreateQueue.clear(); -} - -void QtButtonPropertyBrowserPrivate::setExpanded(WidgetItem *item, bool expanded) -{ - if (item->expanded == expanded) - return; - - if (!item->container) - return; - - item->expanded = expanded; - const int row = gridRow(item); - WidgetItem *parent = item->parent; - QGridLayout *l = 0; - if (parent) - l = parent->layout; - else - l = m_mainLayout; - - if (expanded) { - insertRow(l, row + 1); - l->addWidget(item->container, row + 1, 0, 1, 2); - item->container->show(); - } else { - l->removeWidget(item->container); - item->container->hide(); - removeRow(l, row + 1); - } - - item->button->setChecked(expanded); - item->button->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow); -} - -void QtButtonPropertyBrowserPrivate::slotToggled(bool checked) -{ - WidgetItem *item = m_buttonToItem.value(q_ptr->sender()); - if (!item) - return; - - setExpanded(item, checked); - - if (checked) - emit q_ptr->expanded(m_itemToIndex.value(item)); - else - emit q_ptr->collapsed(m_itemToIndex.value(item)); -} - -void QtButtonPropertyBrowserPrivate::updateLater() -{ - QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); -} - -void QtButtonPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) -{ - WidgetItem *afterItem = m_indexToItem.value(afterIndex); - WidgetItem *parentItem = m_indexToItem.value(index->parent()); - - WidgetItem *newItem = new WidgetItem(); - newItem->parent = parentItem; - - QGridLayout *layout = 0; - QWidget *parentWidget = 0; - int row = -1; - if (!afterItem) { - row = 0; - if (parentItem) - parentItem->children.insert(0, newItem); - else - m_children.insert(0, newItem); - } else { - row = gridRow(afterItem) + gridSpan(afterItem); - if (parentItem) - parentItem->children.insert(parentItem->children.indexOf(afterItem) + 1, newItem); - else - m_children.insert(m_children.indexOf(afterItem) + 1, newItem); - } - - if (!parentItem) { - layout = m_mainLayout; - parentWidget = q_ptr; - } else { - if (!parentItem->container) { - m_recreateQueue.removeAll(parentItem); - WidgetItem *grandParent = parentItem->parent; - QGridLayout *l = 0; - const int oldRow = gridRow(parentItem); - if (grandParent) { - l = grandParent->layout; - } else { - l = m_mainLayout; - } - QFrame *container = new QFrame(); - container->setFrameShape(QFrame::Panel); - container->setFrameShadow(QFrame::Raised); - parentItem->container = container; - parentItem->button = createButton(); - m_buttonToItem[parentItem->button] = parentItem; - q_ptr->connect(parentItem->button, SIGNAL(toggled(bool)), q_ptr, SLOT(slotToggled(bool))); - parentItem->layout = new QGridLayout(); - container->setLayout(parentItem->layout); - if (parentItem->label) { - l->removeWidget(parentItem->label); - delete parentItem->label; - parentItem->label = 0; - } - int span = 1; - if (!parentItem->widget && !parentItem->widgetLabel) - span = 2; - l->addWidget(parentItem->button, oldRow, 0, 1, span); - updateItem(parentItem); - } - layout = parentItem->layout; - parentWidget = parentItem->container; - } - - newItem->label = new QLabel(parentWidget); - newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - newItem->widget = createEditor(index->property(), parentWidget); - if (newItem->widget) { - QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); - m_widgetToItem[newItem->widget] = newItem; - } else if (index->property()->hasValue()) { - newItem->widgetLabel = new QLabel(parentWidget); - newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); - } - - insertRow(layout, row); - int span = 1; - if (newItem->widget) - layout->addWidget(newItem->widget, row, 1); - else if (newItem->widgetLabel) - layout->addWidget(newItem->widgetLabel, row, 1); - else - span = 2; - layout->addWidget(newItem->label, row, 0, span, 1); - - m_itemToIndex[newItem] = index; - m_indexToItem[index] = newItem; - - updateItem(newItem); -} - -void QtButtonPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) -{ - WidgetItem *item = m_indexToItem.value(index); - - m_indexToItem.remove(index); - m_itemToIndex.remove(item); - - WidgetItem *parentItem = item->parent; - - const int row = gridRow(item); - - if (parentItem) - parentItem->children.removeAt(parentItem->children.indexOf(item)); - else - m_children.removeAt(m_children.indexOf(item)); - - const int colSpan = gridSpan(item); - - m_buttonToItem.remove(item->button); - - if (item->widget) - delete item->widget; - if (item->label) - delete item->label; - if (item->widgetLabel) - delete item->widgetLabel; - if (item->button) - delete item->button; - if (item->container) - delete item->container; - - if (!parentItem) { - removeRow(m_mainLayout, row); - if (colSpan > 1) - removeRow(m_mainLayout, row); - } else if (parentItem->children.count() != 0) { - removeRow(parentItem->layout, row); - if (colSpan > 1) - removeRow(parentItem->layout, row); - } else { - const WidgetItem *grandParent = parentItem->parent; - QGridLayout *l = 0; - if (grandParent) { - l = grandParent->layout; - } else { - l = m_mainLayout; - } - - const int parentRow = gridRow(parentItem); - const int parentSpan = gridSpan(parentItem); - - l->removeWidget(parentItem->button); - l->removeWidget(parentItem->container); - delete parentItem->button; - delete parentItem->container; - parentItem->button = 0; - parentItem->container = 0; - parentItem->layout = 0; - if (!m_recreateQueue.contains(parentItem)) - m_recreateQueue.append(parentItem); - if (parentSpan > 1) - removeRow(l, parentRow + 1); - - updateLater(); - } - m_recreateQueue.removeAll(item); - - delete item; -} - -void QtButtonPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const -{ - QMap itemToPos; - int idx = 0; - while (idx < layout->count()) { - int r, c, rs, cs; - layout->getItemPosition(idx, &r, &c, &rs, &cs); - if (r >= row) { - itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); - } else { - idx++; - } - } - - const QMap::ConstIterator icend = itemToPos.constEnd(); - for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { - const QRect r = it.value(); - layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); - } -} - -void QtButtonPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const -{ - QMap itemToPos; - int idx = 0; - while (idx < layout->count()) { - int r, c, rs, cs; - layout->getItemPosition(idx, &r, &c, &rs, &cs); - if (r > row) { - itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); - } else { - idx++; - } - } - - const QMap::ConstIterator icend = itemToPos.constEnd(); - for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { - const QRect r = it.value(); - layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); - } -} - -void QtButtonPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) -{ - WidgetItem *item = m_indexToItem.value(index); - - updateItem(item); -} - -void QtButtonPropertyBrowserPrivate::updateItem(WidgetItem *item) -{ - QtProperty *property = m_itemToIndex[item]->property(); - if (item->button) { - QFont font = item->button->font(); - font.setUnderline(property->isModified()); - item->button->setFont(font); - item->button->setText(property->propertyName()); - item->button->setToolTip(property->toolTip()); - item->button->setStatusTip(property->statusTip()); - item->button->setWhatsThis(property->whatsThis()); - item->button->setEnabled(property->isEnabled()); - } - if (item->label) { - QFont font = item->label->font(); - font.setUnderline(property->isModified()); - item->label->setFont(font); - item->label->setText(property->propertyName()); - item->label->setToolTip(property->toolTip()); - item->label->setStatusTip(property->statusTip()); - item->label->setWhatsThis(property->whatsThis()); - item->label->setEnabled(property->isEnabled()); - } - if (item->widgetLabel) { - QFont font = item->widgetLabel->font(); - font.setUnderline(false); - item->widgetLabel->setFont(font); - item->widgetLabel->setText(property->valueText()); - item->widgetLabel->setToolTip(property->valueText()); - item->widgetLabel->setEnabled(property->isEnabled()); - } - if (item->widget) { - QFont font = item->widget->font(); - font.setUnderline(false); - item->widget->setFont(font); - item->widget->setEnabled(property->isEnabled()); - item->widget->setToolTip(property->valueText()); - } -} - - - -/*! - \class QtButtonPropertyBrowser - - \brief The QtButtonPropertyBrowser class provides a drop down QToolButton - based property browser. - - A property browser is a widget that enables the user to edit a - given set of properties. Each property is represented by a label - specifying the property's name, and an editing widget (e.g. a line - edit or a combobox) holding its value. A property can have zero or - more subproperties. - - QtButtonPropertyBrowser provides drop down button for all nested - properties, i.e. subproperties are enclosed by a container associated with - the drop down button. The parent property's name is displayed as button text. For example: - - \image qtbuttonpropertybrowser.png - - Use the QtAbstractPropertyBrowser API to add, insert and remove - properties from an instance of the QtButtonPropertyBrowser - class. The properties themselves are created and managed by - implementations of the QtAbstractPropertyManager class. - - \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser -*/ - -/*! - \fn void QtButtonPropertyBrowser::collapsed(QtBrowserItem *item) - - This signal is emitted when the \a item is collapsed. - - \sa expanded(), setExpanded() -*/ - -/*! - \fn void QtButtonPropertyBrowser::expanded(QtBrowserItem *item) - - This signal is emitted when the \a item is expanded. - - \sa collapsed(), setExpanded() -*/ - -/*! - Creates a property browser with the given \a parent. -*/ -QtButtonPropertyBrowser::QtButtonPropertyBrowser(QWidget *parent) - : QtAbstractPropertyBrowser(parent) -{ - d_ptr = new QtButtonPropertyBrowserPrivate; - d_ptr->q_ptr = this; - - d_ptr->init(this); -} - -/*! - Destroys this property browser. - - Note that the properties that were inserted into this browser are - \e not destroyed since they may still be used in other - browsers. The properties are owned by the manager that created - them. - - \sa QtProperty, QtAbstractPropertyManager -*/ -QtButtonPropertyBrowser::~QtButtonPropertyBrowser() -{ - const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); - for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) - delete it.key(); - delete d_ptr; -} - -/*! - \reimp -*/ -void QtButtonPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) -{ - d_ptr->propertyInserted(item, afterItem); -} - -/*! - \reimp -*/ -void QtButtonPropertyBrowser::itemRemoved(QtBrowserItem *item) -{ - d_ptr->propertyRemoved(item); -} - -/*! - \reimp -*/ -void QtButtonPropertyBrowser::itemChanged(QtBrowserItem *item) -{ - d_ptr->propertyChanged(item); -} - -/*! - Sets the \a item to either collapse or expanded, depending on the value of \a expanded. - - \sa isExpanded(), expanded(), collapsed() -*/ - -void QtButtonPropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) -{ - QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); - if (itm) - d_ptr->setExpanded(itm, expanded); -} - -/*! - Returns true if the \a item is expanded; otherwise returns false. - - \sa setExpanded() -*/ - -bool QtButtonPropertyBrowser::isExpanded(QtBrowserItem *item) const -{ - QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); - if (itm) - return itm->expanded; - return false; -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qtbuttonpropertybrowser.cpp" diff --git a/src/qtpropertybrowser/src/qtbuttonpropertybrowser.h b/src/qtpropertybrowser/src/qtbuttonpropertybrowser.h deleted file mode 100644 index bdab1485ca..0000000000 --- a/src/qtpropertybrowser/src/qtbuttonpropertybrowser.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTBUTTONPROPERTYBROWSER_H -#define QTBUTTONPROPERTYBROWSER_H - -#include "qtpropertybrowser.h" - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtButtonPropertyBrowserPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtButtonPropertyBrowser : public QtAbstractPropertyBrowser -{ - Q_OBJECT -public: - - QtButtonPropertyBrowser(QWidget *parent = 0); - ~QtButtonPropertyBrowser(); - - void setExpanded(QtBrowserItem *item, bool expanded); - bool isExpanded(QtBrowserItem *item) const; - -Q_SIGNALS: - - void collapsed(QtBrowserItem *item); - void expanded(QtBrowserItem *item); - -protected: - virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); - virtual void itemRemoved(QtBrowserItem *item); - virtual void itemChanged(QtBrowserItem *item); - -private: - - QtButtonPropertyBrowserPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtButtonPropertyBrowser) - Q_DISABLE_COPY(QtButtonPropertyBrowser) - Q_PRIVATE_SLOT(d_func(), void slotUpdate()) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) - Q_PRIVATE_SLOT(d_func(), void slotToggled(bool)) - -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qteditorfactory.cpp b/src/qtpropertybrowser/src/qteditorfactory.cpp deleted file mode 100644 index 284a38dde4..0000000000 --- a/src/qtpropertybrowser/src/qteditorfactory.cpp +++ /dev/null @@ -1,2734 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qteditorfactory.h" -#include "qtpropertybrowserutils_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(Q_CC_MSVC) -# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ -#endif - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -// Set a hard coded left margin to account for the indentation -// of the tree view icon when switching to an editor - -static inline void setupTreeViewEditorMargin(QLayout *lt) -{ - enum { DecorationMargin = 4 }; - if (QApplication::layoutDirection() == Qt::LeftToRight) - lt->setContentsMargins(DecorationMargin, 0, 0, 0); - else - lt->setContentsMargins(0, 0, DecorationMargin, 0); -} - -// ---------- EditorFactoryPrivate : -// Base class for editor factory private classes. Manages mapping of properties to editors and vice versa. - -template -class EditorFactoryPrivate -{ -public: - - typedef QList EditorList; - typedef QMap PropertyToEditorListMap; - typedef QMap EditorToPropertyMap; - - Editor *createEditor(QtProperty *property, QWidget *parent); - void initializeEditor(QtProperty *property, Editor *e); - void slotEditorDestroyed(QObject *object); - - PropertyToEditorListMap m_createdEditors; - EditorToPropertyMap m_editorToProperty; -}; - -template -Editor *EditorFactoryPrivate::createEditor(QtProperty *property, QWidget *parent) -{ - Editor *editor = new Editor(parent); - initializeEditor(property, editor); - return editor; -} - -template -void EditorFactoryPrivate::initializeEditor(QtProperty *property, Editor *editor) -{ - typename PropertyToEditorListMap::iterator it = m_createdEditors.find(property); - if (it == m_createdEditors.end()) - it = m_createdEditors.insert(property, EditorList()); - it.value().append(editor); - m_editorToProperty.insert(editor, property); -} - -template -void EditorFactoryPrivate::slotEditorDestroyed(QObject *object) -{ - const typename EditorToPropertyMap::iterator ecend = m_editorToProperty.end(); - for (typename EditorToPropertyMap::iterator itEditor = m_editorToProperty.begin(); itEditor != ecend; ++itEditor) { - if (itEditor.key() == object) { - Editor *editor = itEditor.key(); - QtProperty *property = itEditor.value(); - const typename PropertyToEditorListMap::iterator pit = m_createdEditors.find(property); - if (pit != m_createdEditors.end()) { - pit.value().removeAll(editor); - if (pit.value().empty()) - m_createdEditors.erase(pit); - } - m_editorToProperty.erase(itEditor); - return; - } - } -} - -// ------------ QtSpinBoxFactory - -class QtSpinBoxFactoryPrivate : public EditorFactoryPrivate -{ - QtSpinBoxFactory *q_ptr; - Q_DECLARE_PUBLIC(QtSpinBoxFactory) -public: - - void slotPropertyChanged(QtProperty *property, int value); - void slotRangeChanged(QtProperty *property, int min, int max); - void slotSingleStepChanged(QtProperty *property, int step); - void slotReadOnlyChanged(QtProperty *property, bool readOnly); - void slotSetValue(int value); -}; - -void QtSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSpinBox *editor = itEditor.next(); - if (editor->value() != value) { - editor->blockSignals(true); - editor->setValue(value); - editor->blockSignals(false); - } - } -} - -void QtSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) -{ - if (!m_createdEditors.contains(property)) - return; - - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSpinBox *editor = itEditor.next(); - editor->blockSignals(true); - editor->setRange(min, max); - editor->setValue(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSpinBox *editor = itEditor.next(); - editor->blockSignals(true); - editor->setSingleStep(step); - editor->blockSignals(false); - } -} - -void QtSpinBoxFactoryPrivate::slotReadOnlyChanged( QtProperty *property, bool readOnly) -{ - if (!m_createdEditors.contains(property)) - return; - - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSpinBox *editor = itEditor.next(); - editor->blockSignals(true); - editor->setReadOnly(readOnly); - editor->blockSignals(false); - } -} - -void QtSpinBoxFactoryPrivate::slotSetValue(int value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) { - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } - } -} - -/*! - \class QtSpinBoxFactory - - \brief The QtSpinBoxFactory class provides QSpinBox widgets for - properties created by QtIntPropertyManager objects. - - \sa QtAbstractEditorFactory, QtIntPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtSpinBoxFactory::QtSpinBoxFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtSpinBoxFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtSpinBoxFactory::~QtSpinBoxFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtSpinBoxFactory::connectPropertyManager(QtIntPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); - connect(manager, SIGNAL(readOnlyChanged(QtProperty *, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtSpinBoxFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QSpinBox *editor = d_ptr->createEditor(property, parent); - editor->setSingleStep(manager->singleStep(property)); - editor->setRange(manager->minimum(property), manager->maximum(property)); - editor->setValue(manager->value(property)); - editor->setKeyboardTracking(false); - editor->setReadOnly(manager->isReadOnly(property)); - - connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtSpinBoxFactory::disconnectPropertyManager(QtIntPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(readOnlyChanged(QtProperty *, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); -} - -// QtSliderFactory - -class QtSliderFactoryPrivate : public EditorFactoryPrivate -{ - QtSliderFactory *q_ptr; - Q_DECLARE_PUBLIC(QtSliderFactory) -public: - void slotPropertyChanged(QtProperty *property, int value); - void slotRangeChanged(QtProperty *property, int min, int max); - void slotSingleStepChanged(QtProperty *property, int step); - void slotSetValue(int value); -}; - -void QtSliderFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSlider *editor = itEditor.next(); - editor->blockSignals(true); - editor->setValue(value); - editor->blockSignals(false); - } -} - -void QtSliderFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) -{ - if (!m_createdEditors.contains(property)) - return; - - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSlider *editor = itEditor.next(); - editor->blockSignals(true); - editor->setRange(min, max); - editor->setValue(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtSliderFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QSlider *editor = itEditor.next(); - editor->blockSignals(true); - editor->setSingleStep(step); - editor->blockSignals(false); - } -} - -void QtSliderFactoryPrivate::slotSetValue(int value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor ) { - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } - } -} - -/*! - \class QtSliderFactory - - \brief The QtSliderFactory class provides QSlider widgets for - properties created by QtIntPropertyManager objects. - - \sa QtAbstractEditorFactory, QtIntPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtSliderFactory::QtSliderFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtSliderFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtSliderFactory::~QtSliderFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtSliderFactory::connectPropertyManager(QtIntPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtSliderFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QSlider *editor = new QSlider(Qt::Horizontal, parent); - d_ptr->initializeEditor(property, editor); - editor->setSingleStep(manager->singleStep(property)); - editor->setRange(manager->minimum(property), manager->maximum(property)); - editor->setValue(manager->value(property)); - - connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtSliderFactory::disconnectPropertyManager(QtIntPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); -} - -// QtSliderFactory - -class QtScrollBarFactoryPrivate : public EditorFactoryPrivate -{ - QtScrollBarFactory *q_ptr; - Q_DECLARE_PUBLIC(QtScrollBarFactory) -public: - void slotPropertyChanged(QtProperty *property, int value); - void slotRangeChanged(QtProperty *property, int min, int max); - void slotSingleStepChanged(QtProperty *property, int step); - void slotSetValue(int value); -}; - -void QtScrollBarFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor( m_createdEditors[property]); - while (itEditor.hasNext()) { - QScrollBar *editor = itEditor.next(); - editor->blockSignals(true); - editor->setValue(value); - editor->blockSignals(false); - } -} - -void QtScrollBarFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) -{ - if (!m_createdEditors.contains(property)) - return; - - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor( m_createdEditors[property]); - while (itEditor.hasNext()) { - QScrollBar *editor = itEditor.next(); - editor->blockSignals(true); - editor->setRange(min, max); - editor->setValue(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtScrollBarFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QScrollBar *editor = itEditor.next(); - editor->blockSignals(true); - editor->setSingleStep(step); - editor->blockSignals(false); - } -} - -void QtScrollBarFactoryPrivate::slotSetValue(int value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtIntPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtScrollBarFactory - - \brief The QtScrollBarFactory class provides QScrollBar widgets for - properties created by QtIntPropertyManager objects. - - \sa QtAbstractEditorFactory, QtIntPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtScrollBarFactory::QtScrollBarFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtScrollBarFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtScrollBarFactory::~QtScrollBarFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtScrollBarFactory::connectPropertyManager(QtIntPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtScrollBarFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QScrollBar *editor = new QScrollBar(Qt::Horizontal, parent); - d_ptr->initializeEditor(property, editor); - editor->setSingleStep(manager->singleStep(property)); - editor->setRange(manager->minimum(property), manager->maximum(property)); - editor->setValue(manager->value(property)); - connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtScrollBarFactory::disconnectPropertyManager(QtIntPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); -} - -// QtCheckBoxFactory - -class QtCheckBoxFactoryPrivate : public EditorFactoryPrivate -{ - QtCheckBoxFactory *q_ptr; - Q_DECLARE_PUBLIC(QtCheckBoxFactory) -public: - void slotPropertyChanged(QtProperty *property, bool value); - void slotTextVisibleChanged(QtProperty *property, bool textVisible); - void slotSetValue(bool value); -}; - -void QtCheckBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, bool value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QtBoolEdit *editor = itEditor.next(); - editor->blockCheckBoxSignals(true); - editor->setChecked(value); - editor->blockCheckBoxSignals(false); - } -} - -void QtCheckBoxFactoryPrivate::slotTextVisibleChanged(QtProperty *property, bool textVisible) -{ - if (!m_createdEditors.contains(property)) - return; - - QtBoolPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QtBoolEdit *editor = itEditor.next(); - editor->setTextVisible(textVisible); - } -} - -void QtCheckBoxFactoryPrivate::slotSetValue(bool value) -{ - QObject *object = q_ptr->sender(); - - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtBoolPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtCheckBoxFactory - - \brief The QtCheckBoxFactory class provides QCheckBox widgets for - properties created by QtBoolPropertyManager objects. - - \sa QtAbstractEditorFactory, QtBoolPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtCheckBoxFactory::QtCheckBoxFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtCheckBoxFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtCheckBoxFactory::~QtCheckBoxFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCheckBoxFactory::connectPropertyManager(QtBoolPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotPropertyChanged(QtProperty *, bool))); - connect(manager, SIGNAL(textVisibleChanged(QtProperty *, bool)), - this, SLOT(slotTextVisibleChanged(QtProperty *, bool))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtCheckBoxFactory::createEditor(QtBoolPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QtBoolEdit *editor = d_ptr->createEditor(property, parent); - editor->setChecked(manager->value(property)); - editor->setTextVisible(manager->textVisible(property)); - - connect(editor, SIGNAL(toggled(bool)), this, SLOT(slotSetValue(bool))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCheckBoxFactory::disconnectPropertyManager(QtBoolPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotPropertyChanged(QtProperty *, bool))); - disconnect(manager, SIGNAL(textVisibleChanged(QtProperty *, bool)), - this, SLOT(slotTextVisibleChanged(QtProperty *, bool))); -} - -// QtDoubleSpinBoxFactory - -class DoubleSpinBoxAnyPrecision : public QDoubleSpinBox -{ - Q_OBJECT - -public: - explicit DoubleSpinBoxAnyPrecision(QWidget *parent = nullptr) - : QDoubleSpinBox(parent) - {} - - QString textFromValue(double val) const override - { - QString text = QDoubleSpinBox::textFromValue(val); - - // remove redundant trailing 0's in case of high precision - if (decimals() > 3) - return removeRedundantTrialingZeros(text); - - return text; - } -}; - -class QtDoubleSpinBoxFactoryPrivate : public EditorFactoryPrivate -{ - QtDoubleSpinBoxFactory *q_ptr; - Q_DECLARE_PUBLIC(QtDoubleSpinBoxFactory) -public: - - void slotPropertyChanged(QtProperty *property, double value); - void slotRangeChanged(QtProperty *property, double min, double max); - void slotSingleStepChanged(QtProperty *property, double step); - void slotDecimalsChanged(QtProperty *property, int prec); - void slotReadOnlyChanged(QtProperty *property, bool readOnly); - void slotSetValue(double value); -}; - -void QtDoubleSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, double value) -{ - const auto editors = m_createdEditors[property]; - for (auto editor : editors) { - if (editor->value() != value) { - editor->blockSignals(true); - editor->setValue(value); - editor->blockSignals(false); - } - } -} - -void QtDoubleSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, - double min, double max) -{ - if (!m_createdEditors.contains(property)) - return; - - QtDoublePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - const auto editors = m_createdEditors[property]; - for (auto editor : editors) { - editor->blockSignals(true); - editor->setRange(min, max); - editor->setValue(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtDoubleSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, double step) -{ - if (!m_createdEditors.contains(property)) - return; - - QtDoublePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - const auto editors = m_createdEditors[property]; - for (auto editor : editors) { - editor->blockSignals(true); - editor->setSingleStep(step); - editor->blockSignals(false); - } -} - -void QtDoubleSpinBoxFactoryPrivate::slotReadOnlyChanged( QtProperty *property, bool readOnly) -{ - if (!m_createdEditors.contains(property)) - return; - - QtDoublePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - const auto editors = m_createdEditors[property]; - for (auto editor : editors) { - editor->blockSignals(true); - editor->setReadOnly(readOnly); - editor->blockSignals(false); - } -} - -void QtDoubleSpinBoxFactoryPrivate::slotDecimalsChanged(QtProperty *property, int prec) -{ - if (!m_createdEditors.contains(property)) - return; - - QtDoublePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - const auto editors = m_createdEditors[property]; - for (auto editor : editors) { - editor->blockSignals(true); - editor->setDecimals(prec); - editor->setValue(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtDoubleSpinBoxFactoryPrivate::slotSetValue(double value) -{ - QObject *object = q_ptr->sender(); - const auto itcend = m_editorToProperty.constEnd(); - for (auto itEditor = m_editorToProperty.constBegin(); itEditor != itcend; ++itEditor) { - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtDoublePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } - } -} - -/*! \class QtDoubleSpinBoxFactory - - \brief The QtDoubleSpinBoxFactory class provides QDoubleSpinBox - widgets for properties created by QtDoublePropertyManager objects. - - \sa QtAbstractEditorFactory, QtDoublePropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtDoubleSpinBoxFactory::QtDoubleSpinBoxFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtDoubleSpinBoxFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtDoubleSpinBoxFactory::~QtDoubleSpinBoxFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDoubleSpinBoxFactory::connectPropertyManager(QtDoublePropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotPropertyChanged(QtProperty *, double))); - connect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), - this, SLOT(slotRangeChanged(QtProperty *, double, double))); - connect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), - this, SLOT(slotSingleStepChanged(QtProperty *, double))); - connect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - connect(manager, SIGNAL(readOnlyChanged(QtProperty *, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtDoubleSpinBoxFactory::createEditor(QtDoublePropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QDoubleSpinBox *editor = d_ptr->createEditor(property, parent); - editor->setSingleStep(manager->singleStep(property)); - editor->setDecimals(manager->decimals(property)); - editor->setRange(manager->minimum(property), manager->maximum(property)); - editor->setValue(manager->value(property)); - editor->setKeyboardTracking(false); - editor->setReadOnly(manager->isReadOnly(property)); - - connect(editor, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDoubleSpinBoxFactory::disconnectPropertyManager(QtDoublePropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotPropertyChanged(QtProperty *, double))); - disconnect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), - this, SLOT(slotRangeChanged(QtProperty *, double, double))); - disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), - this, SLOT(slotSingleStepChanged(QtProperty *, double))); - disconnect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(readOnlyChanged(QtProperty *, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); -} - -// QtLineEditFactory - -class QtLineEditFactoryPrivate : public EditorFactoryPrivate -{ - QtLineEditFactory *q_ptr; - Q_DECLARE_PUBLIC(QtLineEditFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QString &value); - void slotRegExpChanged(QtProperty *property, const QRegularExpression ®Exp); - void slotSetValue(const QString &value); - void slotEchoModeChanged(QtProperty *, int); - void slotReadOnlyChanged(QtProperty *, bool); -}; - -void QtLineEditFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QString &value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor( m_createdEditors[property]); - while (itEditor.hasNext()) { - QLineEdit *editor = itEditor.next(); - if (editor->text() != value) { - editor->blockSignals(true); - editor->setText(value); - editor->blockSignals(false); - } - } -} - -void QtLineEditFactoryPrivate::slotRegExpChanged(QtProperty *property, - const QRegularExpression ®Exp) -{ - if (!m_createdEditors.contains(property)) - return; - - QtStringPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QLineEdit *editor = itEditor.next(); - editor->blockSignals(true); - const QValidator *oldValidator = editor->validator(); - QValidator *newValidator = 0; - if (regExp.isValid()) { - newValidator = new QRegularExpressionValidator(regExp, editor); - } - editor->setValidator(newValidator); - if (oldValidator) - delete oldValidator; - editor->blockSignals(false); - } -} - -void QtLineEditFactoryPrivate::slotEchoModeChanged(QtProperty *property, int echoMode) -{ - if (!m_createdEditors.contains(property)) - return; - - QtStringPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QLineEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setEchoMode((EchoMode)echoMode); - editor->blockSignals(false); - } -} - -void QtLineEditFactoryPrivate::slotReadOnlyChanged( QtProperty *property, bool readOnly) -{ - if (!m_createdEditors.contains(property)) - return; - - QtStringPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QLineEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setReadOnly(readOnly); - editor->blockSignals(false); - } -} - -void QtLineEditFactoryPrivate::slotSetValue(const QString &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtStringPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - - - -/*! - \class QtLineEditFactory - - \brief The QtLineEditFactory class provides QLineEdit widgets for - properties created by QtStringPropertyManager objects. - - \sa QtAbstractEditorFactory, QtStringPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtLineEditFactory::QtLineEditFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtLineEditFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtLineEditFactory::~QtLineEditFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtLineEditFactory::connectPropertyManager(QtStringPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); - connect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegularExpression &)), - this, SLOT(slotRegExpChanged(QtProperty *, const QRegularExpression &))); - connect(manager, SIGNAL(echoModeChanged(QtProperty*, int)), - this, SLOT(slotEchoModeChanged(QtProperty *, int))); - connect(manager, SIGNAL(readOnlyChanged(QtProperty*, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtLineEditFactory::createEditor(QtStringPropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - - QLineEdit *editor = d_ptr->createEditor(property, parent); - editor->setEchoMode((EchoMode)manager->echoMode(property)); - editor->setReadOnly(manager->isReadOnly(property)); - QRegularExpression regExp = manager->regExp(property); - if (regExp.isValid()) { - QValidator *validator = new QRegularExpressionValidator(regExp, editor); - editor->setValidator(validator); - } - editor->setText(manager->value(property)); - - connect(editor, SIGNAL(textChanged(const QString &)), - this, SLOT(slotSetValue(const QString &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtLineEditFactory::disconnectPropertyManager(QtStringPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); - disconnect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegularExpression &)), - this, SLOT(slotRegExpChanged(QtProperty *, const QRegularExpression &))); - disconnect(manager, SIGNAL(echoModeChanged(QtProperty*,int)), - this, SLOT(slotEchoModeChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(readOnlyChanged(QtProperty*, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty *, bool))); - -} - -// QtDateEditFactory - -class QtDateEditFactoryPrivate : public EditorFactoryPrivate -{ - QtDateEditFactory *q_ptr; - Q_DECLARE_PUBLIC(QtDateEditFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QDate &value); - void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); - void slotSetValue(const QDate &value); -}; - -void QtDateEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QDate &value) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QDateEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setDate(value); - editor->blockSignals(false); - } -} - -void QtDateEditFactoryPrivate::slotRangeChanged(QtProperty *property, - const QDate &min, const QDate &max) -{ - if (!m_createdEditors.contains(property)) - return; - - QtDatePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QDateEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setDateRange(min, max); - editor->setDate(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtDateEditFactoryPrivate::slotSetValue(const QDate &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtDatePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtDateEditFactory - - \brief The QtDateEditFactory class provides QDateEdit widgets for - properties created by QtDatePropertyManager objects. - - \sa QtAbstractEditorFactory, QtDatePropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtDateEditFactory::QtDateEditFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtDateEditFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtDateEditFactory::~QtDateEditFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDateEditFactory::connectPropertyManager(QtDatePropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); - connect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), - this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtDateEditFactory::createEditor(QtDatePropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QDateEdit *editor = d_ptr->createEditor(property, parent); - editor->setCalendarPopup(true); - editor->setDateRange(manager->minimum(property), manager->maximum(property)); - editor->setDate(manager->value(property)); - - connect(editor, SIGNAL(dateChanged(const QDate &)), - this, SLOT(slotSetValue(const QDate &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDateEditFactory::disconnectPropertyManager(QtDatePropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); - disconnect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), - this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); -} - -// QtTimeEditFactory - -class QtTimeEditFactoryPrivate : public EditorFactoryPrivate -{ - QtTimeEditFactory *q_ptr; - Q_DECLARE_PUBLIC(QtTimeEditFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QTime &value); - void slotSetValue(const QTime &value); -}; - -void QtTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QTime &value) -{ - if (!m_createdEditors.contains(property)) - return; - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QTimeEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setTime(value); - editor->blockSignals(false); - } -} - -void QtTimeEditFactoryPrivate::slotSetValue(const QTime &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtTimePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtTimeEditFactory - - \brief The QtTimeEditFactory class provides QTimeEdit widgets for - properties created by QtTimePropertyManager objects. - - \sa QtAbstractEditorFactory, QtTimePropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtTimeEditFactory::QtTimeEditFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtTimeEditFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtTimeEditFactory::~QtTimeEditFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtTimeEditFactory::connectPropertyManager(QtTimePropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtTimeEditFactory::createEditor(QtTimePropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QTimeEdit *editor = d_ptr->createEditor(property, parent); - editor->setTime(manager->value(property)); - - connect(editor, SIGNAL(timeChanged(const QTime &)), - this, SLOT(slotSetValue(const QTime &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtTimeEditFactory::disconnectPropertyManager(QtTimePropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); -} - -// QtDateTimeEditFactory - -class QtDateTimeEditFactoryPrivate : public EditorFactoryPrivate -{ - QtDateTimeEditFactory *q_ptr; - Q_DECLARE_PUBLIC(QtDateTimeEditFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QDateTime &value); - void slotSetValue(const QDateTime &value); - -}; - -void QtDateTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QDateTime &value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QDateTimeEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setDateTime(value); - editor->blockSignals(false); - } -} - -void QtDateTimeEditFactoryPrivate::slotSetValue(const QDateTime &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtDateTimePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtDateTimeEditFactory - - \brief The QtDateTimeEditFactory class provides QDateTimeEdit - widgets for properties created by QtDateTimePropertyManager objects. - - \sa QtAbstractEditorFactory, QtDateTimePropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtDateTimeEditFactory::QtDateTimeEditFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtDateTimeEditFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtDateTimeEditFactory::~QtDateTimeEditFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDateTimeEditFactory::connectPropertyManager(QtDateTimePropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtDateTimeEditFactory::createEditor(QtDateTimePropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QDateTimeEdit *editor = d_ptr->createEditor(property, parent); - editor->setDateTime(manager->value(property)); - - connect(editor, SIGNAL(dateTimeChanged(const QDateTime &)), - this, SLOT(slotSetValue(const QDateTime &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtDateTimeEditFactory::disconnectPropertyManager(QtDateTimePropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); -} - -// QtKeySequenceEditorFactory - -class QtKeySequenceEditorFactoryPrivate : public EditorFactoryPrivate -{ - QtKeySequenceEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtKeySequenceEditorFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QKeySequence &value); - void slotSetValue(const QKeySequence &value); -}; - -void QtKeySequenceEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QKeySequence &value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QKeySequenceEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setKeySequence(value); - editor->blockSignals(false); - } -} - -void QtKeySequenceEditorFactoryPrivate::slotSetValue(const QKeySequence &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtKeySequencePropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtKeySequenceEditorFactory - - \brief The QtKeySequenceEditorFactory class provides editor - widgets for properties created by QtKeySequencePropertyManager objects. - - \sa QtAbstractEditorFactory -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtKeySequenceEditorFactory::QtKeySequenceEditorFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtKeySequenceEditorFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtKeySequenceEditorFactory::~QtKeySequenceEditorFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtKeySequenceEditorFactory::connectPropertyManager(QtKeySequencePropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtKeySequenceEditorFactory::createEditor(QtKeySequencePropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QKeySequenceEdit *editor = d_ptr->createEditor(property, parent); - editor->setKeySequence(manager->value(property)); - - connect(editor, SIGNAL(keySequenceChanged(const QKeySequence &)), - this, SLOT(slotSetValue(const QKeySequence &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtKeySequenceEditorFactory::disconnectPropertyManager(QtKeySequencePropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); -} - -// QtCharEdit - -class QtCharEdit : public QWidget -{ - Q_OBJECT -public: - explicit QtCharEdit(QWidget *parent = 0); - - QChar value() const; - bool eventFilter(QObject *o, QEvent *e); -public Q_SLOTS: - void setValue(const QChar &value); -Q_SIGNALS: - void valueChanged(const QChar &value); -protected: - void focusInEvent(QFocusEvent *e); - void focusOutEvent(QFocusEvent *e); - void keyPressEvent(QKeyEvent *e); - void keyReleaseEvent(QKeyEvent *e); - void paintEvent(QPaintEvent *); - bool event(QEvent *e); -private slots: - void slotClearChar(); -private: - void handleKeyEvent(QKeyEvent *e); - - QChar m_value; - QLineEdit *m_lineEdit; -}; - -QtCharEdit::QtCharEdit(QWidget *parent) - : QWidget(parent), m_lineEdit(new QLineEdit(this)) -{ - QHBoxLayout *layout = new QHBoxLayout(this); - layout->addWidget(m_lineEdit); - layout->setContentsMargins(0, 0, 0, 0); - m_lineEdit->installEventFilter(this); - m_lineEdit->setReadOnly(true); - m_lineEdit->setFocusProxy(this); - setFocusPolicy(m_lineEdit->focusPolicy()); - setAttribute(Qt::WA_InputMethodEnabled); -} - -bool QtCharEdit::eventFilter(QObject *o, QEvent *e) -{ - if (o == m_lineEdit && e->type() == QEvent::ContextMenu) { - QContextMenuEvent *c = static_cast(e); - QMenu *menu = m_lineEdit->createStandardContextMenu(); - QList actions = menu->actions(); - QListIterator itAction(actions); - while (itAction.hasNext()) { - QAction *action = itAction.next(); - action->setShortcut(QKeySequence()); - QString actionString = action->text(); - const int pos = actionString.lastIndexOf(QLatin1Char('\t')); - if (pos > 0) - actionString = actionString.remove(pos, actionString.length() - pos); - action->setText(actionString); - } - QAction *actionBefore = 0; - if (actions.count() > 0) - actionBefore = actions[0]; - QAction *clearAction = new QAction(tr("Clear Char"), menu); - menu->insertAction(actionBefore, clearAction); - menu->insertSeparator(actionBefore); - clearAction->setEnabled(!m_value.isNull()); - connect(clearAction, SIGNAL(triggered()), this, SLOT(slotClearChar())); - menu->exec(c->globalPos()); - delete menu; - e->accept(); - return true; - } - - return QWidget::eventFilter(o, e); -} - -void QtCharEdit::slotClearChar() -{ - if (m_value.isNull()) - return; - setValue(QChar()); - emit valueChanged(m_value); -} - -void QtCharEdit::handleKeyEvent(QKeyEvent *e) -{ - const int key = e->key(); - switch (key) { - case Qt::Key_Control: - case Qt::Key_Shift: - case Qt::Key_Meta: - case Qt::Key_Alt: - case Qt::Key_Super_L: - case Qt::Key_Return: - return; - default: - break; - } - - const QString text = e->text(); - if (text.size() != 1) - return; - - const QChar c = text.at(0); - if (!c.isPrint()) - return; - - if (m_value == c) - return; - - m_value = c; - const QString str = m_value.isNull() ? QString() : QString(m_value); - m_lineEdit->setText(str); - e->accept(); - emit valueChanged(m_value); -} - -void QtCharEdit::setValue(const QChar &value) -{ - if (value == m_value) - return; - - m_value = value; - QString str = value.isNull() ? QString() : QString(value); - m_lineEdit->setText(str); -} - -QChar QtCharEdit::value() const -{ - return m_value; -} - -void QtCharEdit::focusInEvent(QFocusEvent *e) -{ - m_lineEdit->event(e); - m_lineEdit->selectAll(); - QWidget::focusInEvent(e); -} - -void QtCharEdit::focusOutEvent(QFocusEvent *e) -{ - m_lineEdit->event(e); - QWidget::focusOutEvent(e); -} - -void QtCharEdit::keyPressEvent(QKeyEvent *e) -{ - handleKeyEvent(e); - e->accept(); -} - -void QtCharEdit::keyReleaseEvent(QKeyEvent *e) -{ - m_lineEdit->event(e); -} - -void QtCharEdit::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -bool QtCharEdit::event(QEvent *e) -{ - switch(e->type()) { - case QEvent::Shortcut: - case QEvent::ShortcutOverride: - case QEvent::KeyRelease: - e->accept(); - return true; - default: - break; - } - return QWidget::event(e); -} - -// QtCharEditorFactory - -class QtCharEditorFactoryPrivate : public EditorFactoryPrivate -{ - QtCharEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtCharEditorFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QChar &value); - void slotSetValue(const QChar &value); - -}; - -void QtCharEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QChar &value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QtCharEdit *editor = itEditor.next(); - editor->blockSignals(true); - editor->setValue(value); - editor->blockSignals(false); - } -} - -void QtCharEditorFactoryPrivate::slotSetValue(const QChar &value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtCharPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtCharEditorFactory - - \brief The QtCharEditorFactory class provides editor - widgets for properties created by QtCharPropertyManager objects. - - \sa QtAbstractEditorFactory -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtCharEditorFactory::QtCharEditorFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtCharEditorFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtCharEditorFactory::~QtCharEditorFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCharEditorFactory::connectPropertyManager(QtCharPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtCharEditorFactory::createEditor(QtCharPropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QtCharEdit *editor = d_ptr->createEditor(property, parent); - editor->setValue(manager->value(property)); - - connect(editor, SIGNAL(valueChanged(const QChar &)), - this, SLOT(slotSetValue(const QChar &))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCharEditorFactory::disconnectPropertyManager(QtCharPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); -} - -// QtEnumEditorFactory - -class QtEnumEditorFactoryPrivate : public EditorFactoryPrivate -{ - QtEnumEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtEnumEditorFactory) -public: - - void slotPropertyChanged(QtProperty *property, int value); - void slotEnumNamesChanged(QtProperty *property, const QStringList &); - void slotEnumIconsChanged(QtProperty *property, const QMap &); - void slotSetValue(int value); -}; - -void QtEnumEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) -{ - if (!m_createdEditors.contains(property)) - return; - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QComboBox *editor = itEditor.next(); - editor->blockSignals(true); - editor->setCurrentIndex(value); - editor->blockSignals(false); - } -} - -void QtEnumEditorFactoryPrivate::slotEnumNamesChanged(QtProperty *property, - const QStringList &enumNames) -{ - if (!m_createdEditors.contains(property)) - return; - - QtEnumPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - QMap enumIcons = manager->enumIcons(property); - - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QComboBox *editor = itEditor.next(); - editor->blockSignals(true); - editor->clear(); - editor->addItems(enumNames); - const int nameCount = enumNames.count(); - for (int i = 0; i < nameCount; i++) - editor->setItemIcon(i, enumIcons.value(i)); - editor->setCurrentIndex(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtEnumEditorFactoryPrivate::slotEnumIconsChanged(QtProperty *property, - const QMap &enumIcons) -{ - if (!m_createdEditors.contains(property)) - return; - - QtEnumPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - - const QStringList enumNames = manager->enumNames(property); - QListIterator itEditor(m_createdEditors[property]); - while (itEditor.hasNext()) { - QComboBox *editor = itEditor.next(); - editor->blockSignals(true); - const int nameCount = enumNames.count(); - for (int i = 0; i < nameCount; i++) - editor->setItemIcon(i, enumIcons.value(i)); - editor->setCurrentIndex(manager->value(property)); - editor->blockSignals(false); - } -} - -void QtEnumEditorFactoryPrivate::slotSetValue(int value) -{ - QObject *object = q_ptr->sender(); - const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtEnumPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtEnumEditorFactory - - \brief The QtEnumEditorFactory class provides QComboBox widgets for - properties created by QtEnumPropertyManager objects. - - \sa QtAbstractEditorFactory, QtEnumPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtEnumEditorFactory::QtEnumEditorFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtEnumEditorFactoryPrivate(); - d_ptr->q_ptr = this; - -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtEnumEditorFactory::~QtEnumEditorFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtEnumEditorFactory::connectPropertyManager(QtEnumPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - connect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), - this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtEnumEditorFactory::createEditor(QtEnumPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QComboBox *editor = d_ptr->createEditor(property, parent); - editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); - editor->setMinimumContentsLength(1); - editor->view()->setTextElideMode(Qt::ElideRight); - QStringList enumNames = manager->enumNames(property); - editor->addItems(enumNames); - QMap enumIcons = manager->enumIcons(property); - const int enumNamesCount = enumNames.count(); - for (int i = 0; i < enumNamesCount; i++) - editor->setItemIcon(i, enumIcons.value(i)); - editor->setCurrentIndex(manager->value(property)); - - connect(editor, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetValue(int))); - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtEnumEditorFactory::disconnectPropertyManager(QtEnumPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotPropertyChanged(QtProperty *, int))); - disconnect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), - this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); -} - -// QtCursorEditorFactory - -Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) - -class QtCursorEditorFactoryPrivate -{ - QtCursorEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtCursorEditorFactory) -public: - QtCursorEditorFactoryPrivate(); - - void slotPropertyChanged(QtProperty *property, const QCursor &cursor); - void slotEnumChanged(QtProperty *property, int value); - void slotEditorDestroyed(QObject *object); - - QtEnumEditorFactory *m_enumEditorFactory; - QtEnumPropertyManager *m_enumPropertyManager; - - QMap m_propertyToEnum; - QMap m_enumToProperty; - QMap > m_enumToEditors; - QMap m_editorToEnum; - bool m_updatingEnum; -}; - -QtCursorEditorFactoryPrivate::QtCursorEditorFactoryPrivate() - : m_updatingEnum(false) -{ - -} - -void QtCursorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, const QCursor &cursor) -{ - // update enum property - QtProperty *enumProp = m_propertyToEnum.value(property); - if (!enumProp) - return; - - m_updatingEnum = true; - m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(cursor)); - m_updatingEnum = false; -} - -void QtCursorEditorFactoryPrivate::slotEnumChanged(QtProperty *property, int value) -{ - if (m_updatingEnum) - return; - // update cursor property - QtProperty *prop = m_enumToProperty.value(property); - if (!prop) - return; - QtCursorPropertyManager *cursorManager = q_ptr->propertyManager(prop); - if (!cursorManager) - return; -#ifndef QT_NO_CURSOR - cursorManager->setValue(prop, QCursor(cursorDatabase()->valueToCursor(value))); -#endif -} - -void QtCursorEditorFactoryPrivate::slotEditorDestroyed(QObject *object) -{ - // remove from m_editorToEnum map; - // remove from m_enumToEditors map; - // if m_enumToEditors doesn't contains more editors delete enum property; - const QMap::ConstIterator ecend = m_editorToEnum.constEnd(); - for (QMap::ConstIterator itEditor = m_editorToEnum.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QWidget *editor = itEditor.key(); - QtProperty *enumProp = itEditor.value(); - m_editorToEnum.remove(editor); - m_enumToEditors[enumProp].removeAll(editor); - if (m_enumToEditors[enumProp].isEmpty()) { - m_enumToEditors.remove(enumProp); - QtProperty *property = m_enumToProperty.value(enumProp); - m_enumToProperty.remove(enumProp); - m_propertyToEnum.remove(property); - delete enumProp; - } - return; - } -} - -/*! - \class QtCursorEditorFactory - - \brief The QtCursorEditorFactory class provides QComboBox widgets for - properties created by QtCursorPropertyManager objects. - - \sa QtAbstractEditorFactory, QtCursorPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtCursorEditorFactory::QtCursorEditorFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtCursorEditorFactoryPrivate(); - d_ptr->q_ptr = this; - - d_ptr->m_enumEditorFactory = new QtEnumEditorFactory(this); - d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); - connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotEnumChanged(QtProperty *, int))); - d_ptr->m_enumEditorFactory->addPropertyManager(d_ptr->m_enumPropertyManager); -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtCursorEditorFactory::~QtCursorEditorFactory() -{ - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCursorEditorFactory::connectPropertyManager(QtCursorPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtCursorEditorFactory::createEditor(QtCursorPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - QtProperty *enumProp = 0; - if (d_ptr->m_propertyToEnum.contains(property)) { - enumProp = d_ptr->m_propertyToEnum[property]; - } else { - enumProp = d_ptr->m_enumPropertyManager->addProperty(property->propertyName()); - d_ptr->m_enumPropertyManager->setEnumNames(enumProp, cursorDatabase()->cursorShapeNames()); - d_ptr->m_enumPropertyManager->setEnumIcons(enumProp, cursorDatabase()->cursorShapeIcons()); -#ifndef QT_NO_CURSOR - d_ptr->m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(manager->value(property))); -#endif - d_ptr->m_propertyToEnum[property] = enumProp; - d_ptr->m_enumToProperty[enumProp] = property; - } - QtAbstractEditorFactoryBase *af = d_ptr->m_enumEditorFactory; - QWidget *editor = af->createEditor(enumProp, parent); - d_ptr->m_enumToEditors[enumProp].append(editor); - d_ptr->m_editorToEnum[editor] = enumProp; - connect(editor, SIGNAL(destroyed(QObject *)), - this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtCursorEditorFactory::disconnectPropertyManager(QtCursorPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), - this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); -} - -// QtColorEditWidget - -class QtColorEditWidget : public QWidget { - Q_OBJECT - -public: - explicit QtColorEditWidget(QWidget *parent); - - bool eventFilter(QObject *obj, QEvent *ev); - -public Q_SLOTS: - void setValue(const QColor &value); - -Q_SIGNALS: - void valueChanged(const QColor &value); - -protected: - void paintEvent(QPaintEvent *); - -private Q_SLOTS: - void buttonClicked(); - -private: - QColor m_color; - QLabel *m_pixmapLabel; - QLabel *m_label; - QToolButton *m_button; -}; - -QtColorEditWidget::QtColorEditWidget(QWidget *parent) : - QWidget(parent), - m_pixmapLabel(new QLabel), - m_label(new QLabel), - m_button(new QToolButton) -{ - QHBoxLayout *lt = new QHBoxLayout(this); - setupTreeViewEditorMargin(lt); - lt->setSpacing(0); - lt->addWidget(m_pixmapLabel); - lt->addWidget(m_label); - lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); - - m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); - m_button->setFixedWidth(20); - setFocusProxy(m_button); - setFocusPolicy(m_button->focusPolicy()); - m_button->setText(tr("...")); - m_button->installEventFilter(this); - connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); - lt->addWidget(m_button); - m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(m_color))); - m_pixmapLabel->setVisible(m_color.isValid()); - m_label->setText(QtPropertyBrowserUtils::colorValueText(m_color)); -} - -void QtColorEditWidget::setValue(const QColor &c) -{ - if (m_color != c) { - m_color = c; - m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(c))); - m_pixmapLabel->setVisible(c.isValid()); - m_label->setText(QtPropertyBrowserUtils::colorValueText(c)); - } -} - -void QtColorEditWidget::buttonClicked() -{ - QColor newColor = QColorDialog::getColor(m_color, this, QString(), QColorDialog::ShowAlphaChannel); - if (newColor.isValid() && newColor != m_color) { - setValue(newColor); - emit valueChanged(m_color); - } -} - -bool QtColorEditWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == m_button) { - switch (ev->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate - switch (static_cast(ev)->key()) { - case Qt::Key_Escape: - case Qt::Key_Enter: - case Qt::Key_Return: - ev->ignore(); - return true; - default: - break; - } - } - break; - default: - break; - } - } - return QWidget::eventFilter(obj, ev); -} - -void QtColorEditWidget::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -// QtColorEditorFactoryPrivate - -class QtColorEditorFactoryPrivate : public EditorFactoryPrivate -{ - QtColorEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtColorEditorFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QColor &value); - void slotSetValue(const QColor &value); -}; - -void QtColorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QColor &value) -{ - const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); - if (it == m_createdEditors.end()) - return; - QListIterator itEditor(it.value()); - - while (itEditor.hasNext()) - itEditor.next()->setValue(value); -} - -void QtColorEditorFactoryPrivate::slotSetValue(const QColor &value) -{ - QObject *object = q_ptr->sender(); - const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtColorPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtColorEditorFactory - - \brief The QtColorEditorFactory class provides color editing for - properties created by QtColorPropertyManager objects. - - \sa QtAbstractEditorFactory, QtColorPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtColorEditorFactory::QtColorEditorFactory(QObject *parent) : - QtAbstractEditorFactory(parent), - d_ptr(new QtColorEditorFactoryPrivate()) -{ - d_ptr->q_ptr = this; -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtColorEditorFactory::~QtColorEditorFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtColorEditorFactory::connectPropertyManager(QtColorPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), - this, SLOT(slotPropertyChanged(QtProperty*,QColor))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtColorEditorFactory::createEditor(QtColorPropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QtColorEditWidget *editor = d_ptr->createEditor(property, parent); - editor->setValue(manager->value(property)); - connect(editor, SIGNAL(valueChanged(QColor)), this, SLOT(slotSetValue(QColor))); - connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtColorEditorFactory::disconnectPropertyManager(QtColorPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), this, SLOT(slotPropertyChanged(QtProperty*,QColor))); -} - -// QtFontEditWidget - -class QtFontEditWidget : public QWidget { - Q_OBJECT - -public: - explicit QtFontEditWidget(QWidget *parent); - - bool eventFilter(QObject *obj, QEvent *ev); - -public Q_SLOTS: - void setValue(const QFont &value); - -Q_SIGNALS: - void valueChanged(const QFont &value); - -protected: - void paintEvent(QPaintEvent *); - -private Q_SLOTS: - void buttonClicked(); - -private: - QFont m_font; - QLabel *m_pixmapLabel; - QLabel *m_label; - QToolButton *m_button; -}; - -QtFontEditWidget::QtFontEditWidget(QWidget *parent) : - QWidget(parent), - m_pixmapLabel(new QLabel), - m_label(new QLabel), - m_button(new QToolButton) -{ - QHBoxLayout *lt = new QHBoxLayout(this); - setupTreeViewEditorMargin(lt); - lt->setSpacing(0); - lt->addWidget(m_pixmapLabel); - lt->addWidget(m_label); - lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); - - m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); - m_button->setFixedWidth(20); - setFocusProxy(m_button); - setFocusPolicy(m_button->focusPolicy()); - m_button->setText(tr("...")); - m_button->installEventFilter(this); - connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); - lt->addWidget(m_button); - m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(m_font)); - m_label->setText(QtPropertyBrowserUtils::fontValueText(m_font)); -} - -void QtFontEditWidget::setValue(const QFont &f) -{ - if (m_font != f) { - m_font = f; - m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(f)); - m_label->setText(QtPropertyBrowserUtils::fontValueText(f)); - } -} - -void QtFontEditWidget::buttonClicked() -{ - bool ok = false; - - QFont oldFont = m_font; - - // Font dialogs generally deal with point sizes. When the font size is set - // in pixels, convert to points. - if (m_font.pixelSize() != -1) - oldFont.setPointSizeF(oldFont.pixelSize() / logicalDpiX() * 72.0); - - QFont newFont = QFontDialog::getFont(&ok, oldFont, this, tr("Select Font")); - if (ok && newFont != oldFont) { - QFont f = m_font; - - // Convert back to pixels when the current font size is set in pixels - if (m_font.pixelSize() != -1 && newFont.pointSize() != -1) { - int pixelSize = qRound(newFont.pointSizeF() / 72.0 * logicalDpiX()); - newFont.setPixelSize(pixelSize); - } - - // prevent mask for unchanged attributes, don't change other attributes (like kerning, etc...) - if (m_font.family() != newFont.family()) - f.setFamily(newFont.family()); - if (m_font.pixelSize() != newFont.pixelSize() && newFont.pixelSize() != -1) - f.setPixelSize(newFont.pixelSize()); - if (m_font.pointSize() != newFont.pointSize() && newFont.pointSize() != -1) - f.setPointSizeF(newFont.pointSizeF()); - if (m_font.bold() != newFont.bold()) - f.setBold(newFont.bold()); - if (m_font.italic() != newFont.italic()) - f.setItalic(newFont.italic()); - if (m_font.underline() != newFont.underline()) - f.setUnderline(newFont.underline()); - if (m_font.strikeOut() != newFont.strikeOut()) - f.setStrikeOut(newFont.strikeOut()); - setValue(f); - emit valueChanged(m_font); - } -} - -bool QtFontEditWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == m_button) { - switch (ev->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate - switch (static_cast(ev)->key()) { - case Qt::Key_Escape: - case Qt::Key_Enter: - case Qt::Key_Return: - ev->ignore(); - return true; - default: - break; - } - } - break; - default: - break; - } - } - return QWidget::eventFilter(obj, ev); -} - -void QtFontEditWidget::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -// QtFontEditorFactoryPrivate - -class QtFontEditorFactoryPrivate : public EditorFactoryPrivate -{ - QtFontEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtFontEditorFactory) -public: - - void slotPropertyChanged(QtProperty *property, const QFont &value); - void slotSetValue(const QFont &value); -}; - -void QtFontEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, - const QFont &value) -{ - const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); - if (it == m_createdEditors.end()) - return; - QListIterator itEditor(it.value()); - - while (itEditor.hasNext()) - itEditor.next()->setValue(value); -} - -void QtFontEditorFactoryPrivate::slotSetValue(const QFont &value) -{ - QObject *object = q_ptr->sender(); - const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); - for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) - if (itEditor.key() == object) { - QtProperty *property = itEditor.value(); - QtFontPropertyManager *manager = q_ptr->propertyManager(property); - if (!manager) - return; - manager->setValue(property, value); - return; - } -} - -/*! - \class QtFontEditorFactory - - \brief The QtFontEditorFactory class provides font editing for - properties created by QtFontPropertyManager objects. - - \sa QtAbstractEditorFactory, QtFontPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtFontEditorFactory::QtFontEditorFactory(QObject *parent) : - QtAbstractEditorFactory(parent), - d_ptr(new QtFontEditorFactoryPrivate()) -{ - d_ptr->q_ptr = this; -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtFontEditorFactory::~QtFontEditorFactory() -{ - qDeleteAll(d_ptr->m_editorToProperty.keys()); - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtFontEditorFactory::connectPropertyManager(QtFontPropertyManager *manager) -{ - connect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), - this, SLOT(slotPropertyChanged(QtProperty*,QFont))); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtFontEditorFactory::createEditor(QtFontPropertyManager *manager, - QtProperty *property, QWidget *parent) -{ - QtFontEditWidget *editor = d_ptr->createEditor(property, parent); - editor->setValue(manager->value(property)); - connect(editor, SIGNAL(valueChanged(QFont)), this, SLOT(slotSetValue(QFont))); - connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); - return editor; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtFontEditorFactory::disconnectPropertyManager(QtFontPropertyManager *manager) -{ - disconnect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), this, SLOT(slotPropertyChanged(QtProperty*,QFont))); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qteditorfactory.cpp" -#include "qteditorfactory.moc" diff --git a/src/qtpropertybrowser/src/qteditorfactory.h b/src/qtpropertybrowser/src/qteditorfactory.h deleted file mode 100644 index c258ae7be8..0000000000 --- a/src/qtpropertybrowser/src/qteditorfactory.h +++ /dev/null @@ -1,406 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTEDITORFACTORY_H -#define QTEDITORFACTORY_H - -#include "qtpropertymanager.h" - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtSpinBoxFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtSpinBoxFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtSpinBoxFactory(QObject *parent = 0); - ~QtSpinBoxFactory(); -protected: - void connectPropertyManager(QtIntPropertyManager *manager); - QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtIntPropertyManager *manager); -private: - QtSpinBoxFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtSpinBoxFactory) - Q_DISABLE_COPY(QtSpinBoxFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotReadOnlyChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtSliderFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtSliderFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtSliderFactory(QObject *parent = 0); - ~QtSliderFactory(); -protected: - void connectPropertyManager(QtIntPropertyManager *manager); - QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtIntPropertyManager *manager); -private: - QtSliderFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtSliderFactory) - Q_DISABLE_COPY(QtSliderFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtScrollBarFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtScrollBarFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtScrollBarFactory(QObject *parent = 0); - ~QtScrollBarFactory(); -protected: - void connectPropertyManager(QtIntPropertyManager *manager); - QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtIntPropertyManager *manager); -private: - QtScrollBarFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtScrollBarFactory) - Q_DISABLE_COPY(QtScrollBarFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtCheckBoxFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtCheckBoxFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtCheckBoxFactory(QObject *parent = 0); - ~QtCheckBoxFactory(); -protected: - void connectPropertyManager(QtBoolPropertyManager *manager); - QWidget *createEditor(QtBoolPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtBoolPropertyManager *manager); -private: - QtCheckBoxFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtCheckBoxFactory) - Q_DISABLE_COPY(QtCheckBoxFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotTextVisibleChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(bool)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtDoubleSpinBoxFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDoubleSpinBoxFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtDoubleSpinBoxFactory(QObject *parent = 0); - ~QtDoubleSpinBoxFactory(); -protected: - void connectPropertyManager(QtDoublePropertyManager *manager); - QWidget *createEditor(QtDoublePropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtDoublePropertyManager *manager); -private: - QtDoubleSpinBoxFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDoubleSpinBoxFactory) - Q_DISABLE_COPY(QtDoubleSpinBoxFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotReadOnlyChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(double)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtLineEditFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtLineEditFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtLineEditFactory(QObject *parent = 0); - ~QtLineEditFactory(); -protected: - void connectPropertyManager(QtStringPropertyManager *manager); - QWidget *createEditor(QtStringPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtStringPropertyManager *manager); -private: - QtLineEditFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtLineEditFactory) - Q_DISABLE_COPY(QtLineEditFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QString &)) - Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegularExpression &)) - Q_PRIVATE_SLOT(d_func(), void slotEchoModeChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotReadOnlyChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QString &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtDateEditFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDateEditFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtDateEditFactory(QObject *parent = 0); - ~QtDateEditFactory(); -protected: - void connectPropertyManager(QtDatePropertyManager *manager); - QWidget *createEditor(QtDatePropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtDatePropertyManager *manager); -private: - QtDateEditFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDateEditFactory) - Q_DISABLE_COPY(QtDateEditFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDate &)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, - const QDate &, const QDate &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDate &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtTimeEditFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtTimeEditFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtTimeEditFactory(QObject *parent = 0); - ~QtTimeEditFactory(); -protected: - void connectPropertyManager(QtTimePropertyManager *manager); - QWidget *createEditor(QtTimePropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtTimePropertyManager *manager); -private: - QtTimeEditFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtTimeEditFactory) - Q_DISABLE_COPY(QtTimeEditFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QTime &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QTime &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtDateTimeEditFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDateTimeEditFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtDateTimeEditFactory(QObject *parent = 0); - ~QtDateTimeEditFactory(); -protected: - void connectPropertyManager(QtDateTimePropertyManager *manager); - QWidget *createEditor(QtDateTimePropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtDateTimePropertyManager *manager); -private: - QtDateTimeEditFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDateTimeEditFactory) - Q_DISABLE_COPY(QtDateTimeEditFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDateTime &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDateTime &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtKeySequenceEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtKeySequenceEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtKeySequenceEditorFactory(QObject *parent = 0); - ~QtKeySequenceEditorFactory(); -protected: - void connectPropertyManager(QtKeySequencePropertyManager *manager); - QWidget *createEditor(QtKeySequencePropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtKeySequencePropertyManager *manager); -private: - QtKeySequenceEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtKeySequenceEditorFactory) - Q_DISABLE_COPY(QtKeySequenceEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QKeySequence &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QKeySequence &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtCharEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtCharEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtCharEditorFactory(QObject *parent = 0); - ~QtCharEditorFactory(); -protected: - void connectPropertyManager(QtCharPropertyManager *manager); - QWidget *createEditor(QtCharPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtCharPropertyManager *manager); -private: - QtCharEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtCharEditorFactory) - Q_DISABLE_COPY(QtCharEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QChar &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QChar &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtEnumEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtEnumEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtEnumEditorFactory(QObject *parent = 0); - ~QtEnumEditorFactory(); -protected: - void connectPropertyManager(QtEnumPropertyManager *manager); - QWidget *createEditor(QtEnumPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtEnumPropertyManager *manager); -private: - QtEnumEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtEnumEditorFactory) - Q_DISABLE_COPY(QtEnumEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, - const QStringList &)) - Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, - const QMap &)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtCursorEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtCursorEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtCursorEditorFactory(QObject *parent = 0); - ~QtCursorEditorFactory(); -protected: - void connectPropertyManager(QtCursorPropertyManager *manager); - QWidget *createEditor(QtCursorPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtCursorPropertyManager *manager); -private: - QtCursorEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtCursorEditorFactory) - Q_DISABLE_COPY(QtCursorEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QCursor &)) - Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) -}; - -class QtColorEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtColorEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtColorEditorFactory(QObject *parent = 0); - ~QtColorEditorFactory(); -protected: - void connectPropertyManager(QtColorPropertyManager *manager); - QWidget *createEditor(QtColorPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtColorPropertyManager *manager); -private: - QtColorEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtColorEditorFactory) - Q_DISABLE_COPY(QtColorEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QColor &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QColor &)) -}; - -class QtFontEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtFontEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtFontEditorFactory(QObject *parent = 0); - ~QtFontEditorFactory(); -protected: - void connectPropertyManager(QtFontPropertyManager *manager); - QWidget *createEditor(QtFontPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtFontPropertyManager *manager); -private: - QtFontEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtFontEditorFactory) - Q_DISABLE_COPY(QtFontEditorFactory) - Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QFont &)) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) - Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QFont &)) -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.cpp b/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.cpp deleted file mode 100644 index b2c4989d5d..0000000000 --- a/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtgroupboxpropertybrowser.h" -#include -#include -#include -#include -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtGroupBoxPropertyBrowserPrivate -{ - QtGroupBoxPropertyBrowser *q_ptr; - Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser) -public: - - void init(QWidget *parent); - - void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); - void propertyRemoved(QtBrowserItem *index); - void propertyChanged(QtBrowserItem *index); - QWidget *createEditor(QtProperty *property, QWidget *parent) const - { return q_ptr->createEditor(property, parent); } - - void slotEditorDestroyed(); - void slotUpdate(); - - struct WidgetItem - { - WidgetItem() : widget(0), label(0), widgetLabel(0), - groupBox(0), layout(0), line(0), parent(0) { } - QWidget *widget; // can be null - QLabel *label; - QLabel *widgetLabel; - QGroupBox *groupBox; - QGridLayout *layout; - QFrame *line; - WidgetItem *parent; - QList children; - }; -private: - void updateLater(); - void updateItem(WidgetItem *item); - void insertRow(QGridLayout *layout, int row) const; - void removeRow(QGridLayout *layout, int row) const; - - bool hasHeader(WidgetItem *item) const; - - QMap m_indexToItem; - QMap m_itemToIndex; - QMap m_widgetToItem; - QGridLayout *m_mainLayout; - QList m_children; - QList m_recreateQueue; -}; - -void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent) -{ - m_mainLayout = new QGridLayout(); - parent->setLayout(m_mainLayout); - QLayoutItem *item = new QSpacerItem(0, 0, - QSizePolicy::Fixed, QSizePolicy::Expanding); - m_mainLayout->addItem(item, 0, 0); -} - -void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed() -{ - QWidget *editor = qobject_cast(q_ptr->sender()); - if (!editor) - return; - if (!m_widgetToItem.contains(editor)) - return; - m_widgetToItem[editor]->widget = 0; - m_widgetToItem.remove(editor); -} - -void QtGroupBoxPropertyBrowserPrivate::slotUpdate() -{ - QListIterator itItem(m_recreateQueue); - while (itItem.hasNext()) { - WidgetItem *item = itItem.next(); - - WidgetItem *par = item->parent; - QWidget *w = 0; - QGridLayout *l = 0; - int oldRow = -1; - if (!par) { - w = q_ptr; - l = m_mainLayout; - oldRow = m_children.indexOf(item); - } else { - w = par->groupBox; - l = par->layout; - oldRow = par->children.indexOf(item); - if (hasHeader(par)) - oldRow += 2; - } - - if (item->widget) { - item->widget->setParent(w); - } else if (item->widgetLabel) { - item->widgetLabel->setParent(w); - } else { - item->widgetLabel = new QLabel(w); - item->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); - item->widgetLabel->setTextFormat(Qt::PlainText); - } - int span = 1; - if (item->widget) - l->addWidget(item->widget, oldRow, 1, 1, 1); - else if (item->widgetLabel) - l->addWidget(item->widgetLabel, oldRow, 1, 1, 1); - else - span = 2; - item->label = new QLabel(w); - item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - l->addWidget(item->label, oldRow, 0, 1, span); - - updateItem(item); - } - m_recreateQueue.clear(); -} - -void QtGroupBoxPropertyBrowserPrivate::updateLater() -{ - QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); -} - -void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) -{ - WidgetItem *afterItem = m_indexToItem.value(afterIndex); - WidgetItem *parentItem = m_indexToItem.value(index->parent()); - - WidgetItem *newItem = new WidgetItem(); - newItem->parent = parentItem; - - QGridLayout *layout = 0; - QWidget *parentWidget = 0; - int row = -1; - if (!afterItem) { - row = 0; - if (parentItem) - parentItem->children.insert(0, newItem); - else - m_children.insert(0, newItem); - } else { - if (parentItem) { - row = parentItem->children.indexOf(afterItem) + 1; - parentItem->children.insert(row, newItem); - } else { - row = m_children.indexOf(afterItem) + 1; - m_children.insert(row, newItem); - } - } - if (parentItem && hasHeader(parentItem)) - row += 2; - - if (!parentItem) { - layout = m_mainLayout; - parentWidget = q_ptr;; - } else { - if (!parentItem->groupBox) { - m_recreateQueue.removeAll(parentItem); - WidgetItem *par = parentItem->parent; - QWidget *w = 0; - QGridLayout *l = 0; - int oldRow = -1; - if (!par) { - w = q_ptr; - l = m_mainLayout; - oldRow = m_children.indexOf(parentItem); - } else { - w = par->groupBox; - l = par->layout; - oldRow = par->children.indexOf(parentItem); - if (hasHeader(par)) - oldRow += 2; - } - parentItem->groupBox = new QGroupBox(w); - parentItem->layout = new QGridLayout(); - parentItem->groupBox->setLayout(parentItem->layout); - if (parentItem->label) { - l->removeWidget(parentItem->label); - delete parentItem->label; - parentItem->label = 0; - } - if (parentItem->widget) { - l->removeWidget(parentItem->widget); - parentItem->widget->setParent(parentItem->groupBox); - parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2); - parentItem->line = new QFrame(parentItem->groupBox); - } else if (parentItem->widgetLabel) { - l->removeWidget(parentItem->widgetLabel); - delete parentItem->widgetLabel; - parentItem->widgetLabel = 0; - } - if (parentItem->line) { - parentItem->line->setFrameShape(QFrame::HLine); - parentItem->line->setFrameShadow(QFrame::Sunken); - parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2); - } - l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2); - updateItem(parentItem); - } - layout = parentItem->layout; - parentWidget = parentItem->groupBox; - } - - newItem->label = new QLabel(parentWidget); - newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - newItem->widget = createEditor(index->property(), parentWidget); - if (!newItem->widget) { - newItem->widgetLabel = new QLabel(parentWidget); - newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); - newItem->widgetLabel->setTextFormat(Qt::PlainText); - } else { - QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); - m_widgetToItem[newItem->widget] = newItem; - } - - insertRow(layout, row); - int span = 1; - if (newItem->widget) - layout->addWidget(newItem->widget, row, 1); - else if (newItem->widgetLabel) - layout->addWidget(newItem->widgetLabel, row, 1); - else - span = 2; - layout->addWidget(newItem->label, row, 0, 1, span); - - m_itemToIndex[newItem] = index; - m_indexToItem[index] = newItem; - - updateItem(newItem); -} - -void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) -{ - WidgetItem *item = m_indexToItem.value(index); - - m_indexToItem.remove(index); - m_itemToIndex.remove(item); - - WidgetItem *parentItem = item->parent; - - int row = -1; - - if (parentItem) { - row = parentItem->children.indexOf(item); - parentItem->children.removeAt(row); - if (hasHeader(parentItem)) - row += 2; - } else { - row = m_children.indexOf(item); - m_children.removeAt(row); - } - - if (item->widget) - delete item->widget; - if (item->label) - delete item->label; - if (item->widgetLabel) - delete item->widgetLabel; - if (item->groupBox) - delete item->groupBox; - - if (!parentItem) { - removeRow(m_mainLayout, row); - } else if (parentItem->children.count() != 0) { - removeRow(parentItem->layout, row); - } else { - WidgetItem *par = parentItem->parent; - QGridLayout *l = 0; - int oldRow = -1; - if (!par) { - l = m_mainLayout; - oldRow = m_children.indexOf(parentItem); - } else { - l = par->layout; - oldRow = par->children.indexOf(parentItem); - if (hasHeader(par)) - oldRow += 2; - } - - if (parentItem->widget) { - parentItem->widget->hide(); - parentItem->widget->setParent(0); - } else if (parentItem->widgetLabel) { - parentItem->widgetLabel->hide(); - parentItem->widgetLabel->setParent(0); - } else { - //parentItem->widgetLabel = new QLabel(w); - } - l->removeWidget(parentItem->groupBox); - delete parentItem->groupBox; - parentItem->groupBox = 0; - parentItem->line = 0; - parentItem->layout = 0; - if (!m_recreateQueue.contains(parentItem)) - m_recreateQueue.append(parentItem); - updateLater(); - } - m_recreateQueue.removeAll(item); - - delete item; -} - -void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const -{ - QMap itemToPos; - int idx = 0; - while (idx < layout->count()) { - int r, c, rs, cs; - layout->getItemPosition(idx, &r, &c, &rs, &cs); - if (r >= row) { - itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); - } else { - idx++; - } - } - - const QMap::ConstIterator icend = itemToPos.constEnd(); - for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { - const QRect r = it.value(); - layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); - } -} - -void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const -{ - QMap itemToPos; - int idx = 0; - while (idx < layout->count()) { - int r, c, rs, cs; - layout->getItemPosition(idx, &r, &c, &rs, &cs); - if (r > row) { - itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); - } else { - idx++; - } - } - - const QMap::ConstIterator icend = itemToPos.constEnd(); - for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { - const QRect r = it.value(); - layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); - } -} - -bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const -{ - if (item->widget) - return true; - return false; -} - -void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) -{ - WidgetItem *item = m_indexToItem.value(index); - - updateItem(item); -} - -void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item) -{ - QtProperty *property = m_itemToIndex[item]->property(); - if (item->groupBox) { - QFont font = item->groupBox->font(); - font.setUnderline(property->isModified()); - item->groupBox->setFont(font); - item->groupBox->setTitle(property->propertyName()); - item->groupBox->setToolTip(property->toolTip()); - item->groupBox->setStatusTip(property->statusTip()); - item->groupBox->setWhatsThis(property->whatsThis()); - item->groupBox->setEnabled(property->isEnabled()); - } - if (item->label) { - QFont font = item->label->font(); - font.setUnderline(property->isModified()); - item->label->setFont(font); - item->label->setText(property->propertyName()); - item->label->setToolTip(property->toolTip()); - item->label->setStatusTip(property->statusTip()); - item->label->setWhatsThis(property->whatsThis()); - item->label->setEnabled(property->isEnabled()); - } - if (item->widgetLabel) { - QFont font = item->widgetLabel->font(); - font.setUnderline(false); - item->widgetLabel->setFont(font); - item->widgetLabel->setText(property->valueText()); - item->widgetLabel->setToolTip(property->valueText()); - item->widgetLabel->setEnabled(property->isEnabled()); - } - if (item->widget) { - QFont font = item->widget->font(); - font.setUnderline(false); - item->widget->setFont(font); - item->widget->setEnabled(property->isEnabled()); - item->widget->setToolTip(property->valueText()); - } - //item->setIcon(1, property->valueIcon()); -} - - - -/*! - \class QtGroupBoxPropertyBrowser - - \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox - based property browser. - - A property browser is a widget that enables the user to edit a - given set of properties. Each property is represented by a label - specifying the property's name, and an editing widget (e.g. a line - edit or a combobox) holding its value. A property can have zero or - more subproperties. - - QtGroupBoxPropertyBrowser provides group boxes for all nested - properties, i.e. subproperties are enclosed by a group box with - the parent property's name as its title. For example: - - \image qtgroupboxpropertybrowser.png - - Use the QtAbstractPropertyBrowser API to add, insert and remove - properties from an instance of the QtGroupBoxPropertyBrowser - class. The properties themselves are created and managed by - implementations of the QtAbstractPropertyManager class. - - \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser -*/ - -/*! - Creates a property browser with the given \a parent. -*/ -QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent) - : QtAbstractPropertyBrowser(parent) -{ - d_ptr = new QtGroupBoxPropertyBrowserPrivate; - d_ptr->q_ptr = this; - - d_ptr->init(this); -} - -/*! - Destroys this property browser. - - Note that the properties that were inserted into this browser are - \e not destroyed since they may still be used in other - browsers. The properties are owned by the manager that created - them. - - \sa QtProperty, QtAbstractPropertyManager -*/ -QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser() -{ - const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); - for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) - delete it.key(); - delete d_ptr; -} - -/*! - \reimp -*/ -void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) -{ - d_ptr->propertyInserted(item, afterItem); -} - -/*! - \reimp -*/ -void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item) -{ - d_ptr->propertyRemoved(item); -} - -/*! - \reimp -*/ -void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item) -{ - d_ptr->propertyChanged(item); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qtgroupboxpropertybrowser.cpp" diff --git a/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.h b/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.h deleted file mode 100644 index 78b4a9b101..0000000000 --- a/src/qtpropertybrowser/src/qtgroupboxpropertybrowser.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTGROUPBOXPROPERTYBROWSER_H -#define QTGROUPBOXPROPERTYBROWSER_H - -#include "qtpropertybrowser.h" - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtGroupBoxPropertyBrowserPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtGroupBoxPropertyBrowser : public QtAbstractPropertyBrowser -{ - Q_OBJECT -public: - - QtGroupBoxPropertyBrowser(QWidget *parent = 0); - ~QtGroupBoxPropertyBrowser(); - -protected: - virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); - virtual void itemRemoved(QtBrowserItem *item); - virtual void itemChanged(QtBrowserItem *item); - -private: - - QtGroupBoxPropertyBrowserPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtGroupBoxPropertyBrowser) - Q_DISABLE_COPY(QtGroupBoxPropertyBrowser) - Q_PRIVATE_SLOT(d_func(), void slotUpdate()) - Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) - -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qtpropertybrowser.cpp b/src/qtpropertybrowser/src/qtpropertybrowser.cpp deleted file mode 100644 index 8bc0ee397a..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowser.cpp +++ /dev/null @@ -1,2079 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtpropertybrowser.h" -#include -#include -#include -#include - -#if defined(Q_CC_MSVC) -# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ -#endif - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtPropertyPrivate -{ -public: - explicit QtPropertyPrivate(QtAbstractPropertyManager *manager) - : m_enabled(true), - m_modified(false), - m_manager(manager) {} - QtProperty *q_ptr; - - QSet m_parentItems; - QList m_subItems; - - QString m_toolTip; - QString m_statusTip; - QString m_whatsThis; - QString m_name; - QColor m_nameColor; - QColor m_valueColor; - bool m_enabled; - bool m_modified; - - QtAbstractPropertyManager * const m_manager; -}; - -class QtAbstractPropertyManagerPrivate -{ - QtAbstractPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtAbstractPropertyManager) -public: - void propertyDestroyed(QtProperty *property); - void propertyChanged(QtProperty *property) const; - void propertyRemoved(QtProperty *property, - QtProperty *parentProperty) const; - void propertyInserted(QtProperty *property, QtProperty *parentProperty, - QtProperty *afterProperty) const; - - QSet m_properties; -}; - -/*! - \class QtProperty - - \brief The QtProperty class encapsulates an instance of a property. - - Properties are created by objects of QtAbstractPropertyManager - subclasses; a manager can create properties of a given type, and - is used in conjunction with the QtAbstractPropertyBrowser class. A - property is always owned by the manager that created it, which can - be retrieved using the propertyManager() function. - - QtProperty contains the most common property attributes, and - provides functions for retrieving as well as setting their values: - - \table - \header \o Getter \o Setter - \row - \o propertyName() \o setPropertyName() - \row - \o statusTip() \o setStatusTip() - \row - \o toolTip() \o setToolTip() - \row - \o whatsThis() \o setWhatsThis() - \row - \o isEnabled() \o setEnabled() - \row - \o isModified() \o setModified() - \row - \o valueText() \o Nop - \row - \o valueIcon() \o Nop - \endtable - - It is also possible to nest properties: QtProperty provides the - addSubProperty(), insertSubProperty() and removeSubProperty() functions to - manipulate the set of subproperties. Use the subProperties() - function to retrieve a property's current set of subproperties. - Note that nested properties are not owned by the parent property, - i.e. each subproperty is owned by the manager that created it. - - \sa QtAbstractPropertyManager, QtBrowserItem -*/ - -/*! - Creates a property with the given \a manager. - - This constructor is only useful when creating a custom QtProperty - subclass (e.g. QtVariantProperty). To create a regular QtProperty - object, use the QtAbstractPropertyManager::addProperty() - function instead. - - \sa QtAbstractPropertyManager::addProperty() -*/ -QtProperty::QtProperty(QtAbstractPropertyManager *manager) -{ - d_ptr = new QtPropertyPrivate(manager); - d_ptr->q_ptr = this; -} - -/*! - Destroys this property. - - Note that subproperties are detached but not destroyed, i.e. they - can still be used in another context. - - \sa QtAbstractPropertyManager::clear() - -*/ -QtProperty::~QtProperty() -{ - QSetIterator itParent(d_ptr->m_parentItems); - while (itParent.hasNext()) { - QtProperty *property = itParent.next(); - property->d_ptr->m_manager->d_ptr->propertyRemoved(this, property); - } - - d_ptr->m_manager->d_ptr->propertyDestroyed(this); - - QListIterator itChild(d_ptr->m_subItems); - while (itChild.hasNext()) { - QtProperty *property = itChild.next(); - property->d_ptr->m_parentItems.remove(this); - } - - itParent.toFront(); - while (itParent.hasNext()) { - QtProperty *property = itParent.next(); - property->d_ptr->m_subItems.removeAll(this); - } - delete d_ptr; -} - -/*! - Returns the set of subproperties. - - Note that subproperties are not owned by \e this property, but by - the manager that created them. - - \sa insertSubProperty(), removeSubProperty() -*/ -QList QtProperty::subProperties() const -{ - return d_ptr->m_subItems; -} - -/*! - Returns a pointer to the manager that owns this property. -*/ -QtAbstractPropertyManager *QtProperty::propertyManager() const -{ - return d_ptr->m_manager; -} - -/*! - Returns the property's tool tip. - - \sa setToolTip() -*/ -QString QtProperty::toolTip() const -{ - return d_ptr->m_toolTip; -} - -/*! - Returns the property's status tip. - - \sa setStatusTip() -*/ -QString QtProperty::statusTip() const -{ - return d_ptr->m_statusTip; -} - -/*! - Returns the property's "What's This" help text. - - \sa setWhatsThis() -*/ -QString QtProperty::whatsThis() const -{ - return d_ptr->m_whatsThis; -} - -/*! - Returns the property's name. - - \sa setPropertyName() -*/ -QString QtProperty::propertyName() const -{ - return d_ptr->m_name; -} - -/*! - Returns the property's name color. - - \sa setNameColor() -*/ -QColor QtProperty::nameColor() const -{ - return d_ptr->m_nameColor; -} - -/*! - Returns the property's value color. - - \sa setValueColor() -*/ -QColor QtProperty::valueColor() const -{ - return d_ptr->m_valueColor; -} - -/*! - Returns whether the property is enabled. - - \sa setEnabled() -*/ -bool QtProperty::isEnabled() const -{ - return d_ptr->m_enabled; -} - -/*! - Returns whether the property is modified. - - \sa setModified() -*/ -bool QtProperty::isModified() const -{ - return d_ptr->m_modified; -} - -/*! - Returns whether the property has a value. - - \sa QtAbstractPropertyManager::hasValue() -*/ -bool QtProperty::hasValue() const -{ - return d_ptr->m_manager->hasValue(this); -} - -/*! - Returns an icon representing the current state of this property. - - If the given property type can not generate such an icon, this - function returns an invalid icon. - - \sa QtAbstractPropertyManager::valueIcon() -*/ -QIcon QtProperty::valueIcon() const -{ - return d_ptr->m_manager->valueIcon(this); -} - -/*! - Returns a string representing the current state of this property. - - If the given property type can not generate such a string, this - function returns an empty string. - - \sa QtAbstractPropertyManager::valueText() -*/ -QString QtProperty::valueText() const -{ - return d_ptr->m_manager->valueText(this); -} - -/*! - Returns the display text according to the echo-mode set on the editor. - - When the editor is a QLineEdit, this will return a string equal to what - is displayed. - - \sa QtAbstractPropertyManager::valueText() -*/ -QString QtProperty::displayText() const -{ - return d_ptr->m_manager->displayText(this); -} - -/*! - Sets the property's tool tip to the given \a text. - - \sa toolTip() -*/ -void QtProperty::setToolTip(const QString &text) -{ - if (d_ptr->m_toolTip == text) - return; - - d_ptr->m_toolTip = text; - propertyChanged(); -} - -/*! - Sets the property's status tip to the given \a text. - - \sa statusTip() -*/ -void QtProperty::setStatusTip(const QString &text) -{ - if (d_ptr->m_statusTip == text) - return; - - d_ptr->m_statusTip = text; - propertyChanged(); -} - -/*! - Sets the property's "What's This" help text to the given \a text. - - \sa whatsThis() -*/ -void QtProperty::setWhatsThis(const QString &text) -{ - if (d_ptr->m_whatsThis == text) - return; - - d_ptr->m_whatsThis = text; - propertyChanged(); -} - -/*! - \fn void QtProperty::setPropertyName(const QString &name) - - Sets the property's name to the given \a name. - - \sa propertyName() -*/ -void QtProperty::setPropertyName(const QString &text) -{ - if (d_ptr->m_name == text) - return; - - d_ptr->m_name = text; - propertyChanged(); -} - -/*! - \fn void QtProperty::setNameColor(const QColor &color) - - Sets the property's name color to the given \a color. - - \sa nameColor() -*/ -void QtProperty::setNameColor(const QColor &color) -{ - if (d_ptr->m_nameColor == color) - return; - - d_ptr->m_nameColor = color; - propertyChanged(); -} - -/*! - \fn void QtProperty::setValueColor(const QColor &color) - - Sets the property's value color to the given \a color. - - \sa valueColor() -*/ -void QtProperty::setValueColor(const QColor &color) -{ - if (d_ptr->m_valueColor == color) - return; - - d_ptr->m_valueColor = color; - propertyChanged(); -} - -/*! - Enables or disables the property according to the passed \a enable value. - - \sa isEnabled() -*/ -void QtProperty::setEnabled(bool enable) -{ - if (d_ptr->m_enabled == enable) - return; - - d_ptr->m_enabled = enable; - propertyChanged(); -} - -/*! - Sets the property's modified state according to the passed \a modified value. - - \sa isModified() -*/ -void QtProperty::setModified(bool modified) -{ - if (d_ptr->m_modified == modified) - return; - - d_ptr->m_modified = modified; - propertyChanged(); -} - -/*! - Appends the given \a property to this property's subproperties. - - If the given \a property already is added, this function does - nothing. - - \sa insertSubProperty(), removeSubProperty() -*/ -void QtProperty::addSubProperty(QtProperty *property) -{ - QtProperty *after = 0; - if (d_ptr->m_subItems.count() > 0) - after = d_ptr->m_subItems.last(); - insertSubProperty(property, after); -} - -/*! - \fn void QtProperty::insertSubProperty(QtProperty *property, QtProperty *precedingProperty) - - Inserts the given \a property after the specified \a - precedingProperty into this property's list of subproperties. If - \a precedingProperty is 0, the specified \a property is inserted - at the beginning of the list. - - If the given \a property already is inserted, this function does - nothing. - - \sa addSubProperty(), removeSubProperty() -*/ -void QtProperty::insertSubProperty(QtProperty *property, - QtProperty *afterProperty) -{ - if (!property) - return; - - if (property == this) - return; - - // traverse all children of item. if this item is a child of item then cannot add. - QList pendingList = property->subProperties(); - QMap visited; - while (!pendingList.isEmpty()) { - QtProperty *i = pendingList.first(); - if (i == this) - return; - pendingList.removeFirst(); - if (visited.contains(i)) - continue; - visited[i] = true; - pendingList += i->subProperties(); - } - - pendingList = subProperties(); - int pos = 0; - int newPos = 0; - QtProperty *properAfterProperty = 0; - while (pos < pendingList.count()) { - QtProperty *i = pendingList.at(pos); - if (i == property) - return; // if item is already inserted in this item then cannot add. - if (i == afterProperty) { - newPos = pos + 1; - properAfterProperty = afterProperty; - } - pos++; - } - - d_ptr->m_subItems.insert(newPos, property); - property->d_ptr->m_parentItems.insert(this); - - d_ptr->m_manager->d_ptr->propertyInserted(property, this, properAfterProperty); -} - -/*! - Removes the given \a property from the list of subproperties - without deleting it. - - \sa addSubProperty(), insertSubProperty() -*/ -void QtProperty::removeSubProperty(QtProperty *property) -{ - if (!property) - return; - - d_ptr->m_manager->d_ptr->propertyRemoved(property, this); - - QList pendingList = subProperties(); - int pos = 0; - while (pos < pendingList.count()) { - if (pendingList.at(pos) == property) { - d_ptr->m_subItems.removeAt(pos); - property->d_ptr->m_parentItems.remove(this); - - return; - } - pos++; - } -} - -/*! - \internal -*/ -void QtProperty::propertyChanged() -{ - d_ptr->m_manager->d_ptr->propertyChanged(this); -} - -//////////////////////////////// - -void QtAbstractPropertyManagerPrivate::propertyDestroyed(QtProperty *property) -{ - if (m_properties.contains(property)) { - emit q_ptr->propertyDestroyed(property); - q_ptr->uninitializeProperty(property); - m_properties.remove(property); - } -} - -void QtAbstractPropertyManagerPrivate::propertyChanged(QtProperty *property) const -{ - emit q_ptr->propertyChanged(property); -} - -void QtAbstractPropertyManagerPrivate::propertyRemoved(QtProperty *property, - QtProperty *parentProperty) const -{ - emit q_ptr->propertyRemoved(property, parentProperty); -} - -void QtAbstractPropertyManagerPrivate::propertyInserted(QtProperty *property, - QtProperty *parentProperty, QtProperty *afterProperty) const -{ - emit q_ptr->propertyInserted(property, parentProperty, afterProperty); -} - -/*! - \class QtAbstractPropertyManager - - \brief The QtAbstractPropertyManager provides an interface for - property managers. - - A manager can create and manage properties of a given type, and is - used in conjunction with the QtAbstractPropertyBrowser class. - - When using a property browser widget, the properties are created - and managed by implementations of the QtAbstractPropertyManager - class. To ensure that the properties' values will be displayed - using suitable editing widgets, the managers are associated with - objects of QtAbstractEditorFactory subclasses. The property browser - will use these associations to determine which factories it should - use to create the preferred editing widgets. - - The QtAbstractPropertyManager class provides common functionality - like creating a property using the addProperty() function, and - retrieving the properties created by the manager using the - properties() function. The class also provides signals that are - emitted when the manager's properties change: propertyInserted(), - propertyRemoved(), propertyChanged() and propertyDestroyed(). - - QtAbstractPropertyManager subclasses are supposed to provide their - own type specific API. Note that several ready-made - implementations are available: - - \list - \o QtBoolPropertyManager - \o QtColorPropertyManager - \o QtDatePropertyManager - \o QtDateTimePropertyManager - \o QtDoublePropertyManager - \o QtEnumPropertyManager - \o QtFlagPropertyManager - \o QtFontPropertyManager - \o QtGroupPropertyManager - \o QtIntPropertyManager - \o QtPointPropertyManager - \o QtRectPropertyManager - \o QtSizePropertyManager - \o QtStringPropertyManager - \o QtTimePropertyManager - \o QtVariantPropertyManager - \endlist - - \sa QtAbstractEditorFactoryBase, QtAbstractPropertyBrowser, QtProperty -*/ - -/*! - \fn void QtAbstractPropertyManager::propertyInserted(QtProperty *newProperty, - QtProperty *parentProperty, QtProperty *precedingProperty) - - This signal is emitted when a new subproperty is inserted into an - existing property, passing pointers to the \a newProperty, \a - parentProperty and \a precedingProperty as parameters. - - If \a precedingProperty is 0, the \a newProperty was inserted at - the beginning of the \a parentProperty's subproperties list. - - Note that signal is emitted only if the \a parentProperty is created - by this manager. - - \sa QtAbstractPropertyBrowser::itemInserted() -*/ - -/*! - \fn void QtAbstractPropertyManager::propertyChanged(QtProperty *property) - - This signal is emitted whenever a property's data changes, passing - a pointer to the \a property as parameter. - - Note that signal is only emitted for properties that are created by - this manager. - - \sa QtAbstractPropertyBrowser::itemChanged() -*/ - -/*! - \fn void QtAbstractPropertyManager::propertyRemoved(QtProperty *property, QtProperty *parent) - - This signal is emitted when a subproperty is removed, passing - pointers to the removed \a property and the \a parent property as - parameters. - - Note that signal is emitted only when the \a parent property is - created by this manager. - - \sa QtAbstractPropertyBrowser::itemRemoved() -*/ - -/*! - \fn void QtAbstractPropertyManager::propertyDestroyed(QtProperty *property) - - This signal is emitted when the specified \a property is about to - be destroyed. - - Note that signal is only emitted for properties that are created - by this manager. - - \sa clear(), uninitializeProperty() -*/ - -/*! - \fn void QtAbstractPropertyBrowser::currentItemChanged(QtBrowserItem *current) - - This signal is emitted when the current item changes. The current item is specified by \a current. - - \sa QtAbstractPropertyBrowser::setCurrentItem() -*/ - -/*! - Creates an abstract property manager with the given \a parent. -*/ -QtAbstractPropertyManager::QtAbstractPropertyManager(QObject *parent) - : QObject(parent) -{ - d_ptr = new QtAbstractPropertyManagerPrivate; - d_ptr->q_ptr = this; - -} - -/*! - Destroys the manager. All properties created by the manager are - destroyed. -*/ -QtAbstractPropertyManager::~QtAbstractPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Destroys all the properties that this manager has created. - - \sa propertyDestroyed(), uninitializeProperty() -*/ -void QtAbstractPropertyManager::clear() const -{ - while (!properties().isEmpty()) { - QSetIterator itProperty(properties()); - QtProperty *prop = itProperty.next(); - delete prop; - } -} - -/*! - Returns the set of properties created by this manager. - - \sa addProperty() -*/ -QSet QtAbstractPropertyManager::properties() const -{ - return d_ptr->m_properties; -} - -/*! - Returns whether the given \a property has a value. - - The default implementation of this function returns true. - - \sa QtProperty::hasValue() -*/ -bool QtAbstractPropertyManager::hasValue(const QtProperty *property) const -{ - Q_UNUSED(property) - return true; -} - -/*! - Returns an icon representing the current state of the given \a - property. - - The default implementation of this function returns an invalid - icon. - - \sa QtProperty::valueIcon() -*/ -QIcon QtAbstractPropertyManager::valueIcon(const QtProperty *property) const -{ - Q_UNUSED(property) - return QIcon(); -} - -/*! - Returns a string representing the current state of the given \a - property. - - The default implementation of this function returns an empty - string. - - \sa QtProperty::valueText() -*/ -QString QtAbstractPropertyManager::valueText(const QtProperty *property) const -{ - Q_UNUSED(property) - return QString(); -} - -/*! - Returns a string representing the current state of the given \a - property. - - The default implementation of this function returns an empty - string. - - \sa QtProperty::valueText() -*/ -QString QtAbstractPropertyManager::displayText(const QtProperty *property) const -{ - Q_UNUSED(property) - return QString(); -} - -/*! - Returns the echo mode representing the current state of the given \a - property. - - The default implementation of this function returns QLineEdit::Normal. - - \sa QtProperty::valueText() -*/ -EchoMode QtAbstractPropertyManager::echoMode(const QtProperty *property) const -{ - Q_UNUSED(property) - return QLineEdit::Normal; -} - -/*! - Creates a property with the given \a name which then is owned by this manager. - - Internally, this function calls the createProperty() and - initializeProperty() functions. - - \sa initializeProperty(), properties() -*/ -QtProperty *QtAbstractPropertyManager::addProperty(const QString &name) -{ - QtProperty *property = createProperty(); - if (property) { - property->setPropertyName(name); - d_ptr->m_properties.insert(property); - initializeProperty(property); - } - return property; -} - -/*! - Creates a property. - - The base implementation produce QtProperty instances; Reimplement - this function to make this manager produce objects of a QtProperty - subclass. - - \sa addProperty(), initializeProperty() -*/ -QtProperty *QtAbstractPropertyManager::createProperty() -{ - return new QtProperty(this); -} - -/*! - \fn void QtAbstractPropertyManager::initializeProperty(QtProperty *property) = 0 - - This function is called whenever a new valid property pointer has - been created, passing the pointer as parameter. - - The purpose is to let the manager know that the \a property has - been created so that it can provide additional attributes for the - new property, e.g. QtIntPropertyManager adds \l - {QtIntPropertyManager::value()}{value}, \l - {QtIntPropertyManager::minimum()}{minimum} and \l - {QtIntPropertyManager::maximum()}{maximum} attributes. Since each manager - subclass adds type specific attributes, this function is pure - virtual and must be reimplemented when deriving from the - QtAbstractPropertyManager class. - - \sa addProperty(), createProperty() -*/ - -/*! - This function is called just before the specified \a property is destroyed. - - The purpose is to let the property manager know that the \a - property is being destroyed so that it can remove the property's - additional attributes. - - \sa clear(), propertyDestroyed() -*/ -void QtAbstractPropertyManager::uninitializeProperty(QtProperty *property) -{ - Q_UNUSED(property) -} - -//////////////////////////////////// - -/*! - \class QtAbstractEditorFactoryBase - - \brief The QtAbstractEditorFactoryBase provides an interface for - editor factories. - - An editor factory is a class that is able to create an editing - widget of a specified type (e.g. line edits or comboboxes) for a - given QtProperty object, and it is used in conjunction with the - QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. - - When using a property browser widget, the properties are created - and managed by implementations of the QtAbstractPropertyManager - class. To ensure that the properties' values will be displayed - using suitable editing widgets, the managers are associated with - objects of QtAbstractEditorFactory subclasses. The property browser - will use these associations to determine which factories it should - use to create the preferred editing widgets. - - Typically, an editor factory is created by subclassing the - QtAbstractEditorFactory template class which inherits - QtAbstractEditorFactoryBase. But note that several ready-made - implementations are available: - - \list - \o QtCheckBoxFactory - \o QtDateEditFactory - \o QtDateTimeEditFactory - \o QtDoubleSpinBoxFactory - \o QtEnumEditorFactory - \o QtLineEditFactory - \o QtScrollBarFactory - \o QtSliderFactory - \o QtSpinBoxFactory - \o QtTimeEditFactory - \o QtVariantEditorFactory - \endlist - - \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser -*/ - -/*! - \fn virtual QWidget *QtAbstractEditorFactoryBase::createEditor(QtProperty *property, - QWidget *parent) = 0 - - Creates an editing widget (with the given \a parent) for the given - \a property. - - This function is reimplemented in QtAbstractEditorFactory template class - which also provides a pure virtual convenience overload of this - function enabling access to the property's manager. - - \sa QtAbstractEditorFactory::createEditor() -*/ - -/*! - \fn QtAbstractEditorFactoryBase::QtAbstractEditorFactoryBase(QObject *parent = 0) - - Creates an abstract editor factory with the given \a parent. -*/ - -/*! - \fn virtual void QtAbstractEditorFactoryBase::breakConnection(QtAbstractPropertyManager *manager) = 0 - - \internal - - Detaches property manager from factory. - This method is reimplemented in QtAbstractEditorFactory template subclass. - You don't need to reimplement it in your subclasses. Instead implement more convenient - QtAbstractEditorFactory::disconnectPropertyManager() which gives you access to particular manager subclass. -*/ - -/*! - \fn virtual void QtAbstractEditorFactoryBase::managerDestroyed(QObject *manager) = 0 - - \internal - - This method is called when property manager is being destroyed. - Basically it notifies factory not to produce editors for properties owned by \a manager. - You don't need to reimplement it in your subclass. This method is implemented in - QtAbstractEditorFactory template subclass. -*/ - -/*! - \class QtAbstractEditorFactory - - \brief The QtAbstractEditorFactory is the base template class for editor - factories. - - An editor factory is a class that is able to create an editing - widget of a specified type (e.g. line edits or comboboxes) for a - given QtProperty object, and it is used in conjunction with the - QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. - - Note that the QtAbstractEditorFactory functions are using the - PropertyManager template argument class which can be any - QtAbstractPropertyManager subclass. For example: - - \code - QtSpinBoxFactory *factory; - QSet managers = factory->propertyManagers(); - \endcode - - Note that QtSpinBoxFactory by definition creates editing widgets - \e only for properties created by QtIntPropertyManager. - - When using a property browser widget, the properties are created - and managed by implementations of the QtAbstractPropertyManager - class. To ensure that the properties' values will be displayed - using suitable editing widgets, the managers are associated with - objects of QtAbstractEditorFactory subclasses. The property browser will - use these associations to determine which factories it should use - to create the preferred editing widgets. - - A QtAbstractEditorFactory object is capable of producing editors for - several property managers at the same time. To create an - association between this factory and a given manager, use the - addPropertyManager() function. Use the removePropertyManager() function to make - this factory stop producing editors for a given property - manager. Use the propertyManagers() function to retrieve the set of - managers currently associated with this factory. - - Several ready-made implementations of the QtAbstractEditorFactory class - are available: - - \list - \o QtCheckBoxFactory - \o QtDateEditFactory - \o QtDateTimeEditFactory - \o QtDoubleSpinBoxFactory - \o QtEnumEditorFactory - \o QtLineEditFactory - \o QtScrollBarFactory - \o QtSliderFactory - \o QtSpinBoxFactory - \o QtTimeEditFactory - \o QtVariantEditorFactory - \endlist - - When deriving from the QtAbstractEditorFactory class, several pure virtual - functions must be implemented: the connectPropertyManager() function is - used by the factory to connect to the given manager's signals, the - createEditor() function is supposed to create an editor for the - given property controlled by the given manager, and finally the - disconnectPropertyManager() function is used by the factory to disconnect - from the specified manager's signals. - - \sa QtAbstractEditorFactoryBase, QtAbstractPropertyManager -*/ - -/*! - \fn QtAbstractEditorFactory::QtAbstractEditorFactory(QObject *parent = 0) - - Creates an editor factory with the given \a parent. - - \sa addPropertyManager() -*/ - -/*! - \fn QWidget *QtAbstractEditorFactory::createEditor(QtProperty *property, QWidget *parent) - - Creates an editing widget (with the given \a parent) for the given - \a property. -*/ - -/*! - \fn void QtAbstractEditorFactory::addPropertyManager(PropertyManager *manager) - - Adds the given \a manager to this factory's set of managers, - making this factory produce editing widgets for properties created - by the given manager. - - The PropertyManager type is a template argument class, and represents the chosen - QtAbstractPropertyManager subclass. - - \sa propertyManagers(), removePropertyManager() -*/ - -/*! - \fn void QtAbstractEditorFactory::removePropertyManager(PropertyManager *manager) - - Removes the given \a manager from this factory's set of - managers. The PropertyManager type is a template argument class, and may be - any QtAbstractPropertyManager subclass. - - \sa propertyManagers(), addPropertyManager() -*/ - -/*! - \fn virtual void QtAbstractEditorFactory::connectPropertyManager(PropertyManager *manager) = 0 - - Connects this factory to the given \a manager's signals. The - PropertyManager type is a template argument class, and represents - the chosen QtAbstractPropertyManager subclass. - - This function is used internally by the addPropertyManager() function, and - makes it possible to update an editing widget when the associated - property's data changes. This is typically done in custom slots - responding to the signals emitted by the property's manager, - e.g. QtIntPropertyManager::valueChanged() and - QtIntPropertyManager::rangeChanged(). - - \sa propertyManagers(), disconnectPropertyManager() -*/ - -/*! - \fn virtual QWidget *QtAbstractEditorFactory::createEditor(PropertyManager *manager, QtProperty *property, - QWidget *parent) = 0 - - Creates an editing widget with the given \a parent for the - specified \a property created by the given \a manager. The - PropertyManager type is a template argument class, and represents - the chosen QtAbstractPropertyManager subclass. - - This function must be implemented in derived classes: It is - recommended to store a pointer to the widget and map it to the - given \a property, since the widget must be updated whenever the - associated property's data changes. This is typically done in - custom slots responding to the signals emitted by the property's - manager, e.g. QtIntPropertyManager::valueChanged() and - QtIntPropertyManager::rangeChanged(). - - \sa connectPropertyManager() -*/ - -/*! - \fn virtual void QtAbstractEditorFactory::disconnectPropertyManager(PropertyManager *manager) = 0 - - Disconnects this factory from the given \a manager's signals. The - PropertyManager type is a template argument class, and represents - the chosen QtAbstractPropertyManager subclass. - - This function is used internally by the removePropertyManager() function. - - \sa propertyManagers(), connectPropertyManager() -*/ - -/*! - \fn QSet QtAbstractEditorFactory::propertyManagers() const - - Returns the factory's set of associated managers. The - PropertyManager type is a template argument class, and represents - the chosen QtAbstractPropertyManager subclass. - - \sa addPropertyManager(), removePropertyManager() -*/ - -/*! - \fn PropertyManager *QtAbstractEditorFactory::propertyManager(QtProperty *property) const - - Returns the property manager for the given \a property, or 0 if - the given \a property doesn't belong to any of this factory's - registered managers. - - The PropertyManager type is a template argument class, and represents the chosen - QtAbstractPropertyManager subclass. - - \sa propertyManagers() -*/ - -/*! - \fn virtual void QtAbstractEditorFactory::managerDestroyed(QObject *manager) - - \internal - \reimp -*/ - -//////////////////////////////////// -class QtBrowserItemPrivate -{ -public: - QtBrowserItemPrivate(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) - : m_browser(browser), m_property(property), m_parent(parent), q_ptr(0) {} - - void addChild(QtBrowserItem *index, QtBrowserItem *after); - void removeChild(QtBrowserItem *index); - - QtAbstractPropertyBrowser * const m_browser; - QtProperty *m_property; - QtBrowserItem *m_parent; - - QtBrowserItem *q_ptr; - - QList m_children; - -}; - -void QtBrowserItemPrivate::addChild(QtBrowserItem *index, QtBrowserItem *after) -{ - if (m_children.contains(index)) - return; - int idx = m_children.indexOf(after) + 1; // we insert after returned idx, if it was -1 then we set idx to 0; - m_children.insert(idx, index); -} - -void QtBrowserItemPrivate::removeChild(QtBrowserItem *index) -{ - m_children.removeAll(index); -} - - -/*! - \class QtBrowserItem - - \brief The QtBrowserItem class represents a property in - a property browser instance. - - Browser items are created whenever a QtProperty is inserted to the - property browser. A QtBrowserItem uniquely identifies a - browser's item. Thus, if the same QtProperty is inserted multiple - times, each occurrence gets its own unique QtBrowserItem. The - items are owned by QtAbstractPropertyBrowser and automatically - deleted when they are removed from the browser. - - You can traverse a browser's properties by calling parent() and - children(). The property and the browser associated with an item - are available as property() and browser(). - - \sa QtAbstractPropertyBrowser, QtProperty -*/ - -/*! - Returns the property which is accosiated with this item. Note that - several items can be associated with the same property instance in - the same property browser. - - \sa QtAbstractPropertyBrowser::items() -*/ - -QtProperty *QtBrowserItem::property() const -{ - return d_ptr->m_property; -} - -/*! - Returns the parent item of \e this item. Returns 0 if \e this item - is associated with top-level property in item's property browser. - - \sa children() -*/ - -QtBrowserItem *QtBrowserItem::parent() const -{ - return d_ptr->m_parent; -} - -/*! - Returns the children items of \e this item. The properties - reproduced from children items are always the same as - reproduced from associated property' children, for example: - - \code - QtBrowserItem *item; - QList childrenItems = item->children(); - - QList childrenProperties = item->property()->subProperties(); - \endcode - - The \e childrenItems list represents the same list as \e childrenProperties. -*/ - -QList QtBrowserItem::children() const -{ - return d_ptr->m_children; -} - -/*! - Returns the property browser which owns \e this item. -*/ - -QtAbstractPropertyBrowser *QtBrowserItem::browser() const -{ - return d_ptr->m_browser; -} - -QtBrowserItem::QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) -{ - d_ptr = new QtBrowserItemPrivate(browser, property, parent); - d_ptr->q_ptr = this; -} - -QtBrowserItem::~QtBrowserItem() -{ - delete d_ptr; -} - - -//////////////////////////////////// - -typedef QMap > Map1; -typedef QMap > > Map2; -Q_GLOBAL_STATIC(Map1, m_viewToManagerToFactory) -Q_GLOBAL_STATIC(Map2, m_managerToFactoryToViews) - -class QtAbstractPropertyBrowserPrivate -{ - QtAbstractPropertyBrowser *q_ptr; - Q_DECLARE_PUBLIC(QtAbstractPropertyBrowser) -public: - QtAbstractPropertyBrowserPrivate(); - - void insertSubTree(QtProperty *property, - QtProperty *parentProperty); - void removeSubTree(QtProperty *property, - QtProperty *parentProperty); - void createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty); - void removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty); - QtBrowserItem *createBrowserIndex(QtProperty *property, QtBrowserItem *parentIndex, QtBrowserItem *afterIndex); - void removeBrowserIndex(QtBrowserItem *index); - void clearIndex(QtBrowserItem *index); - - void slotPropertyInserted(QtProperty *property, - QtProperty *parentProperty, QtProperty *afterProperty); - void slotPropertyRemoved(QtProperty *property, QtProperty *parentProperty); - void slotPropertyDestroyed(QtProperty *property); - void slotPropertyDataChanged(QtProperty *property); - - QList m_subItems; - QMap > m_managerToProperties; - QMap > m_propertyToParents; - - QMap m_topLevelPropertyToIndex; - QList m_topLevelIndexes; - QMap > m_propertyToIndexes; - - QtBrowserItem *m_currentItem; -}; - -QtAbstractPropertyBrowserPrivate::QtAbstractPropertyBrowserPrivate() : - m_currentItem(0) -{ -} - -void QtAbstractPropertyBrowserPrivate::insertSubTree(QtProperty *property, - QtProperty *parentProperty) -{ - if (m_propertyToParents.contains(property)) { - // property was already inserted, so its manager is connected - // and all its children are inserted and theirs managers are connected - // we just register new parent (parent has to be new). - m_propertyToParents[property].append(parentProperty); - // don't need to update m_managerToProperties map since - // m_managerToProperties[manager] already contains property. - return; - } - QtAbstractPropertyManager *manager = property->propertyManager(); - if (m_managerToProperties[manager].isEmpty()) { - // connect manager's signals - q_ptr->connect(manager, SIGNAL(propertyInserted(QtProperty *, - QtProperty *, QtProperty *)), - q_ptr, SLOT(slotPropertyInserted(QtProperty *, - QtProperty *, QtProperty *))); - q_ptr->connect(manager, SIGNAL(propertyRemoved(QtProperty *, - QtProperty *)), - q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - q_ptr->connect(manager, SIGNAL(propertyDestroyed(QtProperty *)), - q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); - q_ptr->connect(manager, SIGNAL(propertyChanged(QtProperty *)), - q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); - } - m_managerToProperties[manager].append(property); - m_propertyToParents[property].append(parentProperty); - - QList subList = property->subProperties(); - QListIterator itSub(subList); - while (itSub.hasNext()) { - QtProperty *subProperty = itSub.next(); - insertSubTree(subProperty, property); - } -} - -void QtAbstractPropertyBrowserPrivate::removeSubTree(QtProperty *property, - QtProperty *parentProperty) -{ - if (!m_propertyToParents.contains(property)) { - // ASSERT - return; - } - - m_propertyToParents[property].removeAll(parentProperty); - if (!m_propertyToParents[property].isEmpty()) - return; - - m_propertyToParents.remove(property); - QtAbstractPropertyManager *manager = property->propertyManager(); - m_managerToProperties[manager].removeAll(property); - if (m_managerToProperties[manager].isEmpty()) { - // disconnect manager's signals - q_ptr->disconnect(manager, SIGNAL(propertyInserted(QtProperty *, - QtProperty *, QtProperty *)), - q_ptr, SLOT(slotPropertyInserted(QtProperty *, - QtProperty *, QtProperty *))); - q_ptr->disconnect(manager, SIGNAL(propertyRemoved(QtProperty *, - QtProperty *)), - q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - q_ptr->disconnect(manager, SIGNAL(propertyDestroyed(QtProperty *)), - q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); - q_ptr->disconnect(manager, SIGNAL(propertyChanged(QtProperty *)), - q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); - - m_managerToProperties.remove(manager); - } - - QList subList = property->subProperties(); - QListIterator itSub(subList); - while (itSub.hasNext()) { - QtProperty *subProperty = itSub.next(); - removeSubTree(subProperty, property); - } -} - -void QtAbstractPropertyBrowserPrivate::createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty) -{ - QMap parentToAfter; - if (afterProperty) { - QMap >::ConstIterator it = - m_propertyToIndexes.find(afterProperty); - if (it == m_propertyToIndexes.constEnd()) - return; - - QList indexes = it.value(); - QListIterator itIndex(indexes); - while (itIndex.hasNext()) { - QtBrowserItem *idx = itIndex.next(); - QtBrowserItem *parentIdx = idx->parent(); - if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) - parentToAfter[idx->parent()] = idx; - } - } else if (parentProperty) { - QMap >::ConstIterator it = - m_propertyToIndexes.find(parentProperty); - if (it == m_propertyToIndexes.constEnd()) - return; - - QList indexes = it.value(); - QListIterator itIndex(indexes); - while (itIndex.hasNext()) { - QtBrowserItem *idx = itIndex.next(); - parentToAfter[idx] = 0; - } - } else { - parentToAfter[0] = 0; - } - - const QMap::ConstIterator pcend = parentToAfter.constEnd(); - for (QMap::ConstIterator it = parentToAfter.constBegin(); it != pcend; ++it) - createBrowserIndex(property, it.key(), it.value()); -} - -QtBrowserItem *QtAbstractPropertyBrowserPrivate::createBrowserIndex(QtProperty *property, - QtBrowserItem *parentIndex, QtBrowserItem *afterIndex) -{ - QtBrowserItem *newIndex = new QtBrowserItem(q_ptr, property, parentIndex); - if (parentIndex) { - parentIndex->d_ptr->addChild(newIndex, afterIndex); - } else { - m_topLevelPropertyToIndex[property] = newIndex; - m_topLevelIndexes.insert(m_topLevelIndexes.indexOf(afterIndex) + 1, newIndex); - } - m_propertyToIndexes[property].append(newIndex); - - q_ptr->itemInserted(newIndex, afterIndex); - - QList subItems = property->subProperties(); - QListIterator itChild(subItems); - QtBrowserItem *afterChild = 0; - while (itChild.hasNext()) { - QtProperty *child = itChild.next(); - afterChild = createBrowserIndex(child, newIndex, afterChild); - } - return newIndex; -} - -void QtAbstractPropertyBrowserPrivate::removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty) -{ - QList toRemove; - QMap >::ConstIterator it = - m_propertyToIndexes.find(property); - if (it == m_propertyToIndexes.constEnd()) - return; - - QList indexes = it.value(); - QListIterator itIndex(indexes); - while (itIndex.hasNext()) { - QtBrowserItem *idx = itIndex.next(); - QtBrowserItem *parentIdx = idx->parent(); - if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) - toRemove.append(idx); - } - - QListIterator itRemove(toRemove); - while (itRemove.hasNext()) { - QtBrowserItem *index = itRemove.next(); - removeBrowserIndex(index); - } -} - -void QtAbstractPropertyBrowserPrivate::removeBrowserIndex(QtBrowserItem *index) -{ - QList children = index->children(); - for (int i = children.count(); i > 0; i--) { - removeBrowserIndex(children.at(i - 1)); - } - - q_ptr->itemRemoved(index); - - if (index->parent()) { - index->parent()->d_ptr->removeChild(index); - } else { - m_topLevelPropertyToIndex.remove(index->property()); - m_topLevelIndexes.removeAll(index); - } - - QtProperty *property = index->property(); - - m_propertyToIndexes[property].removeAll(index); - if (m_propertyToIndexes[property].isEmpty()) - m_propertyToIndexes.remove(property); - - delete index; -} - -void QtAbstractPropertyBrowserPrivate::clearIndex(QtBrowserItem *index) -{ - QList children = index->children(); - QListIterator itChild(children); - while (itChild.hasNext()) { - clearIndex(itChild.next()); - } - delete index; -} - -void QtAbstractPropertyBrowserPrivate::slotPropertyInserted(QtProperty *property, - QtProperty *parentProperty, QtProperty *afterProperty) -{ - if (!m_propertyToParents.contains(parentProperty)) - return; - createBrowserIndexes(property, parentProperty, afterProperty); - insertSubTree(property, parentProperty); - //q_ptr->propertyInserted(property, parentProperty, afterProperty); -} - -void QtAbstractPropertyBrowserPrivate::slotPropertyRemoved(QtProperty *property, - QtProperty *parentProperty) -{ - if (!m_propertyToParents.contains(parentProperty)) - return; - removeSubTree(property, parentProperty); // this line should be probably moved down after propertyRemoved call - //q_ptr->propertyRemoved(property, parentProperty); - removeBrowserIndexes(property, parentProperty); -} - -void QtAbstractPropertyBrowserPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (!m_subItems.contains(property)) - return; - q_ptr->removeProperty(property); -} - -void QtAbstractPropertyBrowserPrivate::slotPropertyDataChanged(QtProperty *property) -{ - if (!m_propertyToParents.contains(property)) - return; - - QMap >::ConstIterator it = - m_propertyToIndexes.find(property); - if (it == m_propertyToIndexes.constEnd()) - return; - - QList indexes = it.value(); - QListIterator itIndex(indexes); - while (itIndex.hasNext()) { - QtBrowserItem *idx = itIndex.next(); - q_ptr->itemChanged(idx); - } - //q_ptr->propertyChanged(property); -} - -/*! - \class QtAbstractPropertyBrowser - - \brief QtAbstractPropertyBrowser provides a base class for - implementing property browsers. - - A property browser is a widget that enables the user to edit a - given set of properties. Each property is represented by a label - specifying the property's name, and an editing widget (e.g. a line - edit or a combobox) holding its value. A property can have zero or - more subproperties. - - \image qtpropertybrowser.png - - The top level properties can be retrieved using the - properties() function. To traverse each property's - subproperties, use the QtProperty::subProperties() function. In - addition, the set of top level properties can be manipulated using - the addProperty(), insertProperty() and removeProperty() - functions. Note that the QtProperty class provides a corresponding - set of functions making it possible to manipulate the set of - subproperties as well. - - To remove all the properties from the property browser widget, use - the clear() function. This function will clear the editor, but it - will not delete the properties since they can still be used in - other editors. - - The properties themselves are created and managed by - implementations of the QtAbstractPropertyManager class. A manager - can handle (i.e. create and manage) properties of a given type. In - the property browser the managers are associated with - implementations of the QtAbstractEditorFactory: A factory is a - class able to create an editing widget of a specified type. - - When using a property browser widget, managers must be created for - each of the required property types before the properties - themselves can be created. To ensure that the properties' values - will be displayed using suitable editing widgets, the managers - must be associated with objects of the preferred factory - implementations using the setFactoryForManager() function. The - property browser will use these associations to determine which - factory it should use to create the preferred editing widget. - - Note that a factory can be associated with many managers, but a - manager can only be associated with one single factory within the - context of a single property browser. The associations between - managers and factories can at any time be removed using the - unsetFactoryForManager() function. - - Whenever the property data changes or a property is inserted or - removed, the itemChanged(), itemInserted() or - itemRemoved() functions are called, respectively. These - functions must be reimplemented in derived classes in order to - update the property browser widget. Be aware that some property - instances can appear several times in an abstract tree - structure. For example: - - \table 100% - \row - \o - \code - QtProperty *property1, *property2, *property3; - - property2->addSubProperty(property1); - property3->addSubProperty(property2); - - QtAbstractPropertyBrowser *editor; - - editor->addProperty(property1); - editor->addProperty(property2); - editor->addProperty(property3); - \endcode - \o \image qtpropertybrowser-duplicate.png - \endtable - - The addProperty() function returns a QtBrowserItem that uniquely - identifies the created item. - - To make a property editable in the property browser, the - createEditor() function must be called to provide the - property with a suitable editing widget. - - Note that there are two ready-made property browser - implementations: - - \list - \o QtGroupBoxPropertyBrowser - \o QtTreePropertyBrowser - \endlist - - \sa QtAbstractPropertyManager, QtAbstractEditorFactoryBase -*/ - -/*! - \fn void QtAbstractPropertyBrowser::setFactoryForManager(PropertyManager *manager, - QtAbstractEditorFactory *factory) - - Connects the given \a manager to the given \a factory, ensuring - that properties of the \a manager's type will be displayed with an - editing widget suitable for their value. - - For example: - - \code - QtIntPropertyManager *intManager; - QtDoublePropertyManager *doubleManager; - - QtProperty *myInteger = intManager->addProperty(); - QtProperty *myDouble = doubleManager->addProperty(); - - QtSpinBoxFactory *spinBoxFactory; - QtDoubleSpinBoxFactory *doubleSpinBoxFactory; - - QtAbstractPropertyBrowser *editor; - editor->setFactoryForManager(intManager, spinBoxFactory); - editor->setFactoryForManager(doubleManager, doubleSpinBoxFactory); - - editor->addProperty(myInteger); - editor->addProperty(myDouble); - \endcode - - In this example the \c myInteger property's value is displayed - with a QSpinBox widget, while the \c myDouble property's value is - displayed with a QDoubleSpinBox widget. - - Note that a factory can be associated with many managers, but a - manager can only be associated with one single factory. If the - given \a manager already is associated with another factory, the - old association is broken before the new one established. - - This function ensures that the given \a manager and the given \a - factory are compatible, and it automatically calls the - QtAbstractEditorFactory::addPropertyManager() function if necessary. - - \sa unsetFactoryForManager() -*/ - -/*! - \fn virtual void QtAbstractPropertyBrowser::itemInserted(QtBrowserItem *insertedItem, - QtBrowserItem *precedingItem) = 0 - - This function is called to update the widget whenever a property - is inserted or added to the property browser, passing pointers to - the \a insertedItem of property and the specified - \a precedingItem as parameters. - - If \a precedingItem is 0, the \a insertedItem was put at - the beginning of its parent item's list of subproperties. If - the parent of \a insertedItem is 0, the \a insertedItem was added as a top - level property of \e this property browser. - - This function must be reimplemented in derived classes. Note that - if the \a insertedItem's property has subproperties, this - method will be called for those properties as soon as the current call is finished. - - \sa insertProperty(), addProperty() -*/ - -/*! - \fn virtual void QtAbstractPropertyBrowser::itemRemoved(QtBrowserItem *item) = 0 - - This function is called to update the widget whenever a property - is removed from the property browser, passing the pointer to the - \a item of the property as parameters. The passed \a item is - deleted just after this call is finished. - - If the the parent of \a item is 0, the removed \a item was a - top level property in this editor. - - This function must be reimplemented in derived classes. Note that - if the removed \a item's property has subproperties, this - method will be called for those properties just before the current call is started. - - \sa removeProperty() -*/ - -/*! - \fn virtual void QtAbstractPropertyBrowser::itemChanged(QtBrowserItem *item) = 0 - - This function is called whenever a property's data changes, - passing a pointer to the \a item of property as parameter. - - This function must be reimplemented in derived classes in order to - update the property browser widget whenever a property's name, - tool tip, status tip, "what's this" text, value text or value icon - changes. - - Note that if the property browser contains several occurrences of - the same property, this method will be called once for each - occurrence (with a different item each time). - - \sa QtProperty, items() -*/ - -/*! - Creates an abstract property browser with the given \a parent. -*/ -QtAbstractPropertyBrowser::QtAbstractPropertyBrowser(QWidget *parent) - : QWidget(parent) -{ - d_ptr = new QtAbstractPropertyBrowserPrivate; - d_ptr->q_ptr = this; - -} - -/*! - Destroys the property browser, and destroys all the items that were - created by this property browser. - - Note that the properties that were displayed in the editor are not - deleted since they still can be used in other editors. Neither - does the destructor delete the property managers and editor - factories that were used by this property browser widget unless - this widget was their parent. - - \sa QtAbstractPropertyManager::~QtAbstractPropertyManager() -*/ -QtAbstractPropertyBrowser::~QtAbstractPropertyBrowser() -{ - QList indexes = topLevelItems(); - QListIterator itItem(indexes); - while (itItem.hasNext()) - d_ptr->clearIndex(itItem.next()); - delete d_ptr; -} - -/*! - Returns the property browser's list of top level properties. - - To traverse the subproperties, use the QtProperty::subProperties() - function. - - \sa addProperty(), insertProperty(), removeProperty() -*/ -QList QtAbstractPropertyBrowser::properties() const -{ - return d_ptr->m_subItems; -} - -/*! - Returns the property browser's list of all items associated - with the given \a property. - - There is one item per instance of the property in the browser. - - \sa topLevelItem() -*/ - -QList QtAbstractPropertyBrowser::items(QtProperty *property) const -{ - return d_ptr->m_propertyToIndexes.value(property); -} - -/*! - Returns the top-level items associated with the given \a property. - - Returns 0 if \a property wasn't inserted into this property - browser or isn't a top-level one. - - \sa topLevelItems(), items() -*/ - -QtBrowserItem *QtAbstractPropertyBrowser::topLevelItem(QtProperty *property) const -{ - return d_ptr->m_topLevelPropertyToIndex.value(property); -} - -/*! - Returns the list of top-level items. - - \sa topLevelItem() -*/ - -QList QtAbstractPropertyBrowser::topLevelItems() const -{ - return d_ptr->m_topLevelIndexes; -} - -/*! - Removes all the properties from the editor, but does not delete - them since they can still be used in other editors. - - \sa removeProperty(), QtAbstractPropertyManager::clear() -*/ -void QtAbstractPropertyBrowser::clear() -{ - QList subList = properties(); - QListIterator itSub(subList); - itSub.toBack(); - while (itSub.hasPrevious()) { - QtProperty *property = itSub.previous(); - removeProperty(property); - } -} - -/*! - Appends the given \a property (and its subproperties) to the - property browser's list of top level properties. Returns the item - created by property browser which is associated with the \a property. - In order to get all children items created by the property - browser in this call, the returned item should be traversed. - - If the specified \a property is already added, this function does - nothing and returns 0. - - \sa insertProperty(), QtProperty::addSubProperty(), properties() -*/ -QtBrowserItem *QtAbstractPropertyBrowser::addProperty(QtProperty *property) -{ - QtProperty *afterProperty = 0; - if (d_ptr->m_subItems.count() > 0) - afterProperty = d_ptr->m_subItems.last(); - return insertProperty(property, afterProperty); -} - -/*! - \fn QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, - QtProperty *afterProperty) - - Inserts the given \a property (and its subproperties) after - the specified \a afterProperty in the browser's list of top - level properties. Returns item created by property browser which - is associated with the \a property. In order to get all children items - created by the property browser in this call returned item should be traversed. - - If the specified \a afterProperty is 0, the given \a property is - inserted at the beginning of the list. If \a property is - already inserted, this function does nothing and returns 0. - - \sa addProperty(), QtProperty::insertSubProperty(), properties() -*/ -QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, - QtProperty *afterProperty) -{ - if (!property) - return 0; - - // if item is already inserted in this item then cannot add. - QList pendingList = properties(); - int pos = 0; - int newPos = 0; - while (pos < pendingList.count()) { - QtProperty *prop = pendingList.at(pos); - if (prop == property) - return 0; - if (prop == afterProperty) { - newPos = pos + 1; - } - pos++; - } - d_ptr->createBrowserIndexes(property, 0, afterProperty); - - // traverse inserted subtree and connect to manager's signals - d_ptr->insertSubTree(property, 0); - - d_ptr->m_subItems.insert(newPos, property); - //propertyInserted(property, 0, properAfterProperty); - return topLevelItem(property); -} - -/*! - Removes the specified \a property (and its subproperties) from the - property browser's list of top level properties. All items - that were associated with the given \a property and its children - are deleted. - - Note that the properties are \e not deleted since they can still - be used in other editors. - - \sa clear(), QtProperty::removeSubProperty(), properties() -*/ -void QtAbstractPropertyBrowser::removeProperty(QtProperty *property) -{ - if (!property) - return; - - QList pendingList = properties(); - int pos = 0; - while (pos < pendingList.count()) { - if (pendingList.at(pos) == property) { - d_ptr->m_subItems.removeAt(pos); //perhaps this two lines - d_ptr->removeSubTree(property, 0); //should be moved down after propertyRemoved call. - //propertyRemoved(property, 0); - - d_ptr->removeBrowserIndexes(property, 0); - - // when item is deleted, item will call removeItem for top level items, - // and itemRemoved for nested items. - - return; - } - pos++; - } -} - -/*! - Creates an editing widget (with the given \a parent) for the given - \a property according to the previously established associations - between property managers and editor factories. - - If the property is created by a property manager which was not - associated with any of the existing factories in \e this property - editor, the function returns 0. - - To make a property editable in the property browser, the - createEditor() function must be called to provide the - property with a suitable editing widget. - - Reimplement this function to provide additional decoration for the - editing widgets created by the installed factories. - - \sa setFactoryForManager() -*/ -QWidget *QtAbstractPropertyBrowser::createEditor(QtProperty *property, - QWidget *parent) -{ - QtAbstractEditorFactoryBase *factory = 0; - QtAbstractPropertyManager *manager = property->propertyManager(); - - if (m_viewToManagerToFactory()->contains(this) && - (*m_viewToManagerToFactory())[this].contains(manager)) { - factory = (*m_viewToManagerToFactory())[this][manager]; - } - - if (!factory) - return 0; - return factory->createEditor(property, parent); -} - -bool QtAbstractPropertyBrowser::addFactory(QtAbstractPropertyManager *abstractManager, - QtAbstractEditorFactoryBase *abstractFactory) -{ - bool connectNeeded = false; - if (!m_managerToFactoryToViews()->contains(abstractManager) || - !(*m_managerToFactoryToViews())[abstractManager].contains(abstractFactory)) { - connectNeeded = true; - } else if ((*m_managerToFactoryToViews())[abstractManager][abstractFactory] - .contains(this)) { - return connectNeeded; - } - - if (m_viewToManagerToFactory()->contains(this) && - (*m_viewToManagerToFactory())[this].contains(abstractManager)) { - unsetFactoryForManager(abstractManager); - } - - (*m_managerToFactoryToViews())[abstractManager][abstractFactory].append(this); - (*m_viewToManagerToFactory())[this][abstractManager] = abstractFactory; - - return connectNeeded; -} - -/*! - Removes the association between the given \a manager and the - factory bound to it, automatically calling the - QtAbstractEditorFactory::removePropertyManager() function if necessary. - - \sa setFactoryForManager() -*/ -void QtAbstractPropertyBrowser::unsetFactoryForManager(QtAbstractPropertyManager *manager) -{ - if (!m_viewToManagerToFactory()->contains(this) || - !(*m_viewToManagerToFactory())[this].contains(manager)) { - return; - } - - QtAbstractEditorFactoryBase *abstractFactory = - (*m_viewToManagerToFactory())[this][manager]; - (*m_viewToManagerToFactory())[this].remove(manager); - if ((*m_viewToManagerToFactory())[this].isEmpty()) { - (*m_viewToManagerToFactory()).remove(this); - } - - (*m_managerToFactoryToViews())[manager][abstractFactory].removeAll(this); - if ((*m_managerToFactoryToViews())[manager][abstractFactory].isEmpty()) { - (*m_managerToFactoryToViews())[manager].remove(abstractFactory); - abstractFactory->breakConnection(manager); - if ((*m_managerToFactoryToViews())[manager].isEmpty()) { - (*m_managerToFactoryToViews()).remove(manager); - } - } -} - -/*! - Returns the current item in the property browser. - - \sa setCurrentItem() -*/ -QtBrowserItem *QtAbstractPropertyBrowser::currentItem() const -{ - return d_ptr->m_currentItem; -} - -/*! - Sets the current item in the property browser to \a item. - - \sa currentItem(), currentItemChanged() -*/ -void QtAbstractPropertyBrowser::setCurrentItem(QtBrowserItem *item) -{ - QtBrowserItem *oldItem = d_ptr->m_currentItem; - d_ptr->m_currentItem = item; - if (oldItem != item) - emit currentItemChanged(item); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qtpropertybrowser.cpp" diff --git a/src/qtpropertybrowser/src/qtpropertybrowser.h b/src/qtpropertybrowser/src/qtpropertybrowser.h deleted file mode 100644 index 3a04452c98..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowser.h +++ /dev/null @@ -1,339 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTPROPERTYBROWSER_H -#define QTPROPERTYBROWSER_H - -#include -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -#if defined(Q_OS_WIN) -# if !defined(QT_QTPROPERTYBROWSER_EXPORT) && !defined(QT_QTPROPERTYBROWSER_IMPORT) -# define QT_QTPROPERTYBROWSER_EXPORT -# elif defined(QT_QTPROPERTYBROWSER_IMPORT) -# if defined(QT_QTPROPERTYBROWSER_EXPORT) -# undef QT_QTPROPERTYBROWSER_EXPORT -# endif -# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllimport) -# elif defined(QT_QTPROPERTYBROWSER_EXPORT) -# undef QT_QTPROPERTYBROWSER_EXPORT -# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTPROPERTYBROWSER_EXPORT -#endif - -typedef QLineEdit::EchoMode EchoMode; - -class QtAbstractPropertyManager; -class QtPropertyPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtProperty -{ -public: - virtual ~QtProperty(); - - QList subProperties() const; - - QtAbstractPropertyManager *propertyManager() const; - - QString toolTip() const; - QString statusTip() const; - QString whatsThis() const; - QString propertyName() const; - QColor nameColor() const; - QColor valueColor() const; - bool isEnabled() const; - bool isModified() const; - - bool hasValue() const; - QIcon valueIcon() const; - QString valueText() const; - QString displayText() const; - - void setToolTip(const QString &text); - void setStatusTip(const QString &text); - void setWhatsThis(const QString &text); - void setPropertyName(const QString &text); - void setNameColor(const QColor &color); - void setValueColor(const QColor &color); - void setEnabled(bool enable); - void setModified(bool modified); - - void addSubProperty(QtProperty *property); - void insertSubProperty(QtProperty *property, QtProperty *afterProperty); - void removeSubProperty(QtProperty *property); -protected: - explicit QtProperty(QtAbstractPropertyManager *manager); - void propertyChanged(); -private: - friend class QtAbstractPropertyManager; - QtPropertyPrivate *d_ptr; -}; - -class QtAbstractPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyManager : public QObject -{ - Q_OBJECT -public: - - explicit QtAbstractPropertyManager(QObject *parent = 0); - ~QtAbstractPropertyManager(); - - QSet properties() const; - void clear() const; - - QtProperty *addProperty(const QString &name = QString()); -Q_SIGNALS: - - void propertyInserted(QtProperty *property, - QtProperty *parent, QtProperty *after); - void propertyChanged(QtProperty *property); - void propertyRemoved(QtProperty *property, QtProperty *parent); - void propertyDestroyed(QtProperty *property); -protected: - virtual bool hasValue(const QtProperty *property) const; - virtual QIcon valueIcon(const QtProperty *property) const; - virtual QString valueText(const QtProperty *property) const; - virtual QString displayText(const QtProperty *property) const; - virtual EchoMode echoMode(const QtProperty *) const; - virtual void initializeProperty(QtProperty *property) = 0; - virtual void uninitializeProperty(QtProperty *property); - virtual QtProperty *createProperty(); -private: - friend class QtProperty; - QtAbstractPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtAbstractPropertyManager) - Q_DISABLE_COPY(QtAbstractPropertyManager) -}; - -class QT_QTPROPERTYBROWSER_EXPORT QtAbstractEditorFactoryBase : public QObject -{ - Q_OBJECT -public: - virtual QWidget *createEditor(QtProperty *property, QWidget *parent) = 0; -protected: - explicit QtAbstractEditorFactoryBase(QObject *parent = 0) - : QObject(parent) {} - - virtual void breakConnection(QtAbstractPropertyManager *manager) = 0; -protected Q_SLOTS: - virtual void managerDestroyed(QObject *manager) = 0; - - friend class QtAbstractPropertyBrowser; -}; - -template -class QtAbstractEditorFactory : public QtAbstractEditorFactoryBase -{ -public: - explicit QtAbstractEditorFactory(QObject *parent) : QtAbstractEditorFactoryBase(parent) {} - QWidget *createEditor(QtProperty *property, QWidget *parent) - { - QSetIterator it(m_managers); - while (it.hasNext()) { - PropertyManager *manager = it.next(); - if (manager == property->propertyManager()) { - return createEditor(manager, property, parent); - } - } - return 0; - } - void addPropertyManager(PropertyManager *manager) - { - if (m_managers.contains(manager)) - return; - m_managers.insert(manager); - connectPropertyManager(manager); - connect(manager, SIGNAL(destroyed(QObject *)), - this, SLOT(managerDestroyed(QObject *))); - } - void removePropertyManager(PropertyManager *manager) - { - if (!m_managers.contains(manager)) - return; - disconnect(manager, SIGNAL(destroyed(QObject *)), - this, SLOT(managerDestroyed(QObject *))); - disconnectPropertyManager(manager); - m_managers.remove(manager); - } - QSet propertyManagers() const - { - return m_managers; - } - PropertyManager *propertyManager(QtProperty *property) const - { - QtAbstractPropertyManager *manager = property->propertyManager(); - QSetIterator itManager(m_managers); - while (itManager.hasNext()) { - PropertyManager *m = itManager.next(); - if (m == manager) { - return m; - } - } - return 0; - } -protected: - virtual void connectPropertyManager(PropertyManager *manager) = 0; - virtual QWidget *createEditor(PropertyManager *manager, QtProperty *property, - QWidget *parent) = 0; - virtual void disconnectPropertyManager(PropertyManager *manager) = 0; - void managerDestroyed(QObject *manager) - { - QSetIterator it(m_managers); - while (it.hasNext()) { - PropertyManager *m = it.next(); - if (m == manager) { - m_managers.remove(m); - return; - } - } - } -private: - void breakConnection(QtAbstractPropertyManager *manager) - { - QSetIterator it(m_managers); - while (it.hasNext()) { - PropertyManager *m = it.next(); - if (m == manager) { - removePropertyManager(m); - return; - } - } - } -private: - QSet m_managers; - friend class QtAbstractPropertyEditor; -}; - -class QtAbstractPropertyBrowser; -class QtBrowserItemPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtBrowserItem -{ -public: - QtProperty *property() const; - QtBrowserItem *parent() const; - QList children() const; - QtAbstractPropertyBrowser *browser() const; -private: - explicit QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent); - ~QtBrowserItem(); - QtBrowserItemPrivate *d_ptr; - friend class QtAbstractPropertyBrowserPrivate; -}; - -class QtAbstractPropertyBrowserPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyBrowser : public QWidget -{ - Q_OBJECT -public: - - explicit QtAbstractPropertyBrowser(QWidget *parent = 0); - ~QtAbstractPropertyBrowser(); - - QList properties() const; - QList items(QtProperty *property) const; - QtBrowserItem *topLevelItem(QtProperty *property) const; - QList topLevelItems() const; - void clear(); - - template - void setFactoryForManager(PropertyManager *manager, - QtAbstractEditorFactory *factory) { - QtAbstractPropertyManager *abstractManager = manager; - QtAbstractEditorFactoryBase *abstractFactory = factory; - - if (addFactory(abstractManager, abstractFactory)) - factory->addPropertyManager(manager); - } - - void unsetFactoryForManager(QtAbstractPropertyManager *manager); - - QtBrowserItem *currentItem() const; - void setCurrentItem(QtBrowserItem *); - -Q_SIGNALS: - void currentItemChanged(QtBrowserItem *); - -public Q_SLOTS: - - QtBrowserItem *addProperty(QtProperty *property); - QtBrowserItem *insertProperty(QtProperty *property, QtProperty *afterProperty); - void removeProperty(QtProperty *property); - -protected: - - virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) = 0; - virtual void itemRemoved(QtBrowserItem *item) = 0; - // can be tooltip, statustip, whatsthis, name, icon, text. - virtual void itemChanged(QtBrowserItem *item) = 0; - - virtual QWidget *createEditor(QtProperty *property, QWidget *parent); -private: - - bool addFactory(QtAbstractPropertyManager *abstractManager, - QtAbstractEditorFactoryBase *abstractFactory); - - QtAbstractPropertyBrowserPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtAbstractPropertyBrowser) - Q_DISABLE_COPY(QtAbstractPropertyBrowser) - Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, - QtProperty *, QtProperty *)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, - QtProperty *)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDataChanged(QtProperty *)) - -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif // QTPROPERTYBROWSER_H diff --git a/src/qtpropertybrowser/src/qtpropertybrowser.pri b/src/qtpropertybrowser/src/qtpropertybrowser.pri deleted file mode 100644 index 4ae6e103ed..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowser.pri +++ /dev/null @@ -1,31 +0,0 @@ -include(../common.pri) -greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD - -qtpropertybrowser-uselib:!qtpropertybrowser-buildlib { - LIBS += -L$$QTPROPERTYBROWSER_LIBDIR -l$$QTPROPERTYBROWSER_LIBNAME -} else { - SOURCES += $$PWD/qtpropertybrowser.cpp \ - $$PWD/qtpropertymanager.cpp \ - $$PWD/qteditorfactory.cpp \ - $$PWD/qtvariantproperty.cpp \ - $$PWD/qttreepropertybrowser.cpp \ - $$PWD/qtbuttonpropertybrowser.cpp \ - $$PWD/qtgroupboxpropertybrowser.cpp \ - $$PWD/qtpropertybrowserutils.cpp - HEADERS += $$PWD/qtpropertybrowser.h \ - $$PWD/qtpropertymanager.h \ - $$PWD/qteditorfactory.h \ - $$PWD/qtvariantproperty.h \ - $$PWD/qttreepropertybrowser.h \ - $$PWD/qtbuttonpropertybrowser.h \ - $$PWD/qtgroupboxpropertybrowser.h \ - $$PWD/qtpropertybrowserutils_p.h - RESOURCES += $$PWD/qtpropertybrowser.qrc -} - -win32 { - contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTPROPERTYBROWSER_EXPORT - else:qtpropertybrowser-uselib:DEFINES += QT_QTPROPERTYBROWSER_IMPORT -} diff --git a/src/qtpropertybrowser/src/qtpropertybrowser.qrc b/src/qtpropertybrowser/src/qtpropertybrowser.qrc deleted file mode 100644 index 03e9c5612e..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowser.qrc +++ /dev/null @@ -1,23 +0,0 @@ - - - images/cursor-arrow.png - images/cursor-busy.png - images/cursor-closedhand.png - images/cursor-cross.png - images/cursor-forbidden.png - images/cursor-hand.png - images/cursor-hsplit.png - images/cursor-ibeam.png - images/cursor-openhand.png - images/cursor-sizeall.png - images/cursor-sizeb.png - images/cursor-sizef.png - images/cursor-sizeh.png - images/cursor-sizev.png - images/cursor-uparrow.png - images/cursor-vsplit.png - images/cursor-wait.png - images/cursor-whatsthis.png - - - diff --git a/src/qtpropertybrowser/src/qtpropertybrowserutils.cpp b/src/qtpropertybrowser/src/qtpropertybrowserutils.cpp deleted file mode 100644 index 74755cb1bc..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowserutils.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtpropertybrowserutils_p.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -QtCursorDatabase::QtCursorDatabase() -{ - appendCursor(Qt::ArrowCursor, QCoreApplication::translate("QtCursorDatabase", "Arrow"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-arrow.png"))); - appendCursor(Qt::UpArrowCursor, QCoreApplication::translate("QtCursorDatabase", "Up Arrow"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-uparrow.png"))); - appendCursor(Qt::CrossCursor, QCoreApplication::translate("QtCursorDatabase", "Cross"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-cross.png"))); - appendCursor(Qt::WaitCursor, QCoreApplication::translate("QtCursorDatabase", "Wait"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-wait.png"))); - appendCursor(Qt::IBeamCursor, QCoreApplication::translate("QtCursorDatabase", "IBeam"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-ibeam.png"))); - appendCursor(Qt::SizeVerCursor, QCoreApplication::translate("QtCursorDatabase", "Size Vertical"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-sizev.png"))); - appendCursor(Qt::SizeHorCursor, QCoreApplication::translate("QtCursorDatabase", "Size Horizontal"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-sizeh.png"))); - appendCursor(Qt::SizeFDiagCursor, QCoreApplication::translate("QtCursorDatabase", "Size Backslash"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-sizef.png"))); - appendCursor(Qt::SizeBDiagCursor, QCoreApplication::translate("QtCursorDatabase", "Size Slash"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-sizeb.png"))); - appendCursor(Qt::SizeAllCursor, QCoreApplication::translate("QtCursorDatabase", "Size All"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-sizeall.png"))); - appendCursor(Qt::BlankCursor, QCoreApplication::translate("QtCursorDatabase", "Blank"), - QIcon()); - appendCursor(Qt::SplitVCursor, QCoreApplication::translate("QtCursorDatabase", "Split Vertical"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-vsplit.png"))); - appendCursor(Qt::SplitHCursor, QCoreApplication::translate("QtCursorDatabase", "Split Horizontal"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-hsplit.png"))); - appendCursor(Qt::PointingHandCursor, QCoreApplication::translate("QtCursorDatabase", "Pointing Hand"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-hand.png"))); - appendCursor(Qt::ForbiddenCursor, QCoreApplication::translate("QtCursorDatabase", "Forbidden"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-forbidden.png"))); - appendCursor(Qt::OpenHandCursor, QCoreApplication::translate("QtCursorDatabase", "Open Hand"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-openhand.png"))); - appendCursor(Qt::ClosedHandCursor, QCoreApplication::translate("QtCursorDatabase", "Closed Hand"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-closedhand.png"))); - appendCursor(Qt::WhatsThisCursor, QCoreApplication::translate("QtCursorDatabase", "What's This"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-whatsthis.png"))); - appendCursor(Qt::BusyCursor, QCoreApplication::translate("QtCursorDatabase", "Busy"), - QIcon(QLatin1String(":/qt-project.org/qtpropertybrowser/images/cursor-busy.png"))); -} - -void QtCursorDatabase::clear() -{ - m_cursorNames.clear(); - m_cursorIcons.clear(); - m_valueToCursorShape.clear(); - m_cursorShapeToValue.clear(); -} - -void QtCursorDatabase::appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon) -{ - if (m_cursorShapeToValue.contains(shape)) - return; - const int value = m_cursorNames.count(); - m_cursorNames.append(name); - m_cursorIcons.insert(value, icon); - m_valueToCursorShape.insert(value, shape); - m_cursorShapeToValue.insert(shape, value); -} - -QStringList QtCursorDatabase::cursorShapeNames() const -{ - return m_cursorNames; -} - -QMap QtCursorDatabase::cursorShapeIcons() const -{ - return m_cursorIcons; -} - -QString QtCursorDatabase::cursorToShapeName(const QCursor &cursor) const -{ - int val = cursorToValue(cursor); - if (val >= 0) - return m_cursorNames.at(val); - return QString(); -} - -QIcon QtCursorDatabase::cursorToShapeIcon(const QCursor &cursor) const -{ - int val = cursorToValue(cursor); - return m_cursorIcons.value(val); -} - -int QtCursorDatabase::cursorToValue(const QCursor &cursor) const -{ -#ifndef QT_NO_CURSOR - Qt::CursorShape shape = cursor.shape(); - if (m_cursorShapeToValue.contains(shape)) - return m_cursorShapeToValue[shape]; -#endif - return -1; -} - -#ifndef QT_NO_CURSOR -QCursor QtCursorDatabase::valueToCursor(int value) const -{ - if (m_valueToCursorShape.contains(value)) - return QCursor(m_valueToCursorShape[value]); - return QCursor(); -} -#endif - -QPixmap QtPropertyBrowserUtils::brushValuePixmap(const QBrush &b) -{ - QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); - img.fill(0); - - QPainter painter(&img); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(0, 0, img.width(), img.height(), b); - QColor color = b.color(); - if (color.alpha() != 255) { // indicate alpha by an inset - QBrush opaqueBrush = b; - color.setAlpha(255); - opaqueBrush.setColor(color); - painter.fillRect(img.width() / 4, img.height() / 4, - img.width() / 2, img.height() / 2, opaqueBrush); - } - painter.end(); - return QPixmap::fromImage(img); -} - -QIcon QtPropertyBrowserUtils::brushValueIcon(const QBrush &b) -{ - return QIcon(brushValuePixmap(b)); -} - -QString QtPropertyBrowserUtils::colorValueText(const QColor &c) -{ - if (c.isValid()) { - return QCoreApplication::translate("QtPropertyBrowserUtils", "[%1, %2, %3] (%4)") - .arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha()); - } else { - return QCoreApplication::translate("QtPropertyBrowserUtils", "Not set"); - } -} - -QPixmap QtPropertyBrowserUtils::fontValuePixmap(const QFont &font, int size) -{ - QFont f = font; - QImage img(size, size, QImage::Format_ARGB32_Premultiplied); - img.fill(0); - QPainter p(&img); - p.setRenderHint(QPainter::TextAntialiasing, true); - p.setRenderHint(QPainter::Antialiasing, true); - f.setPixelSize(img.height() - 2); - p.setFont(f); - QTextOption t; - t.setAlignment(Qt::AlignCenter); - p.drawText(img.rect(), QString(QLatin1Char('A')), t); - return QPixmap::fromImage(img); -} - -QIcon QtPropertyBrowserUtils::fontValueIcon(const QFont &f) -{ - QIcon icon(fontValuePixmap(f, 16)); - icon.addPixmap(fontValuePixmap(f, 32)); - return icon; -} - -QString QtPropertyBrowserUtils::fontValueText(const QFont &f) -{ - int size = f.pointSize(); - if (size == -1) - size = f.pixelSize(); - - return QCoreApplication::translate("QtPropertyBrowserUtils", "[%1, %2]") - .arg(f.family()).arg(size); -} - - -QtBoolEdit::QtBoolEdit(QWidget *parent) : - QWidget(parent), - m_checkBox(new QCheckBox(this)), - m_textVisible(true) -{ - QHBoxLayout *lt = new QHBoxLayout; - if (QApplication::layoutDirection() == Qt::LeftToRight) - lt->setContentsMargins(4, 0, 0, 0); - else - lt->setContentsMargins(0, 0, 4, 0); - lt->addWidget(m_checkBox); - setLayout(lt); - connect(m_checkBox, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); - setFocusProxy(m_checkBox); - m_checkBox->setText(tr("True")); -} - -void QtBoolEdit::setTextVisible(bool textVisible) -{ - if (m_textVisible == textVisible) - return; - - m_textVisible = textVisible; - if (m_textVisible) - m_checkBox->setText(isChecked() ? tr("True") : tr("False")); - else - m_checkBox->setText(QString()); -} - -Qt::CheckState QtBoolEdit::checkState() const -{ - return m_checkBox->checkState(); -} - -void QtBoolEdit::setCheckState(Qt::CheckState state) -{ - m_checkBox->setCheckState(state); -} - -bool QtBoolEdit::isChecked() const -{ - return m_checkBox->isChecked(); -} - -void QtBoolEdit::setChecked(bool c) -{ - m_checkBox->setChecked(c); - if (!m_textVisible) - return; - m_checkBox->setText(isChecked() ? tr("True") : tr("False")); -} - -bool QtBoolEdit::blockCheckBoxSignals(bool block) -{ - return m_checkBox->blockSignals(block); -} - -void QtBoolEdit::mousePressEvent(QMouseEvent *event) -{ - if (event->buttons() == Qt::LeftButton) { - m_checkBox->click(); - event->accept(); - } else { - QWidget::mousePressEvent(event); - } -} - -void QtBoolEdit::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - - -/** - * Strips a floating point number representation of redundant trailing zeros. - * Examples: - * - * 0.01000 -> 0.01 - * 3.000 -> 3.0 - */ -QString removeRedundantTrialingZeros(const QString &text) -{ - const QString decimalPoint = QLocale::system().decimalPoint(); - const auto decimalPointIndex = text.lastIndexOf(decimalPoint); - if (decimalPointIndex < 0) // return if there is no decimal point - return text; - - const auto afterDecimalPoint = decimalPointIndex + decimalPoint.length(); - int redundantZeros = 0; - - for (int i = text.length() - 1; i > afterDecimalPoint && text.at(i) == QLatin1Char('0'); --i) - ++redundantZeros; - - return text.left(text.length() - redundantZeros); -} - - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif diff --git a/src/qtpropertybrowser/src/qtpropertybrowserutils_p.h b/src/qtpropertybrowser/src/qtpropertybrowserutils_p.h deleted file mode 100644 index ca90d4e72b..0000000000 --- a/src/qtpropertybrowser/src/qtpropertybrowserutils_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of Qt Designer. This header -// file may change from version to version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QTPROPERTYBROWSERUTILS_H -#define QTPROPERTYBROWSERUTILS_H - -#include -#include -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QMouseEvent; -class QCheckBox; -class QLineEdit; - -class QtCursorDatabase -{ -public: - QtCursorDatabase(); - void clear(); - - QStringList cursorShapeNames() const; - QMap cursorShapeIcons() const; - QString cursorToShapeName(const QCursor &cursor) const; - QIcon cursorToShapeIcon(const QCursor &cursor) const; - int cursorToValue(const QCursor &cursor) const; -#ifndef QT_NO_CURSOR - QCursor valueToCursor(int value) const; -#endif -private: - void appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon); - QStringList m_cursorNames; - QMap m_cursorIcons; - QMap m_valueToCursorShape; - QMap m_cursorShapeToValue; -}; - -class QtPropertyBrowserUtils -{ -public: - static QPixmap brushValuePixmap(const QBrush &b); - static QIcon brushValueIcon(const QBrush &b); - static QString colorValueText(const QColor &c); - static QPixmap fontValuePixmap(const QFont &f, int size = 16); - static QIcon fontValueIcon(const QFont &f); - static QString fontValueText(const QFont &f); -}; - -class QtBoolEdit : public QWidget { - Q_OBJECT -public: - QtBoolEdit(QWidget *parent = 0); - - bool textVisible() const { return m_textVisible; } - void setTextVisible(bool textVisible); - - Qt::CheckState checkState() const; - void setCheckState(Qt::CheckState state); - - bool isChecked() const; - void setChecked(bool c); - - bool blockCheckBoxSignals(bool block); - -Q_SIGNALS: - void toggled(bool); - -protected: - void mousePressEvent(QMouseEvent * event); - void paintEvent(QPaintEvent *); - -private: - QCheckBox *m_checkBox; - bool m_textVisible; -}; - -QString removeRedundantTrialingZeros(const QString &text); - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qtpropertymanager.cpp b/src/qtpropertybrowser/src/qtpropertymanager.cpp deleted file mode 100644 index e1eae9cd4d..0000000000 --- a/src/qtpropertybrowser/src/qtpropertymanager.cpp +++ /dev/null @@ -1,5995 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtpropertymanager.h" -#include "qtpropertybrowserutils_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#if defined(Q_CC_MSVC) -# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ -#endif - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -template -static void setSimpleMinimumData(PrivateData *data, const Value &minVal) -{ - data->minVal = minVal; - if (data->maxVal < data->minVal) - data->maxVal = data->minVal; - - if (data->val < data->minVal) - data->val = data->minVal; -} - -template -static void setSimpleMaximumData(PrivateData *data, const Value &maxVal) -{ - data->maxVal = maxVal; - if (data->minVal > data->maxVal) - data->minVal = data->maxVal; - - if (data->val > data->maxVal) - data->val = data->maxVal; -} - -template -static void setSizeMinimumData(PrivateData *data, const Value &newMinVal) -{ - data->minVal = newMinVal; - if (data->maxVal.width() < data->minVal.width()) - data->maxVal.setWidth(data->minVal.width()); - if (data->maxVal.height() < data->minVal.height()) - data->maxVal.setHeight(data->minVal.height()); - - if (data->val.width() < data->minVal.width()) - data->val.setWidth(data->minVal.width()); - if (data->val.height() < data->minVal.height()) - data->val.setHeight(data->minVal.height()); -} - -template -static void setSizeMaximumData(PrivateData *data, const Value &newMaxVal) -{ - data->maxVal = newMaxVal; - if (data->minVal.width() > data->maxVal.width()) - data->minVal.setWidth(data->maxVal.width()); - if (data->minVal.height() > data->maxVal.height()) - data->minVal.setHeight(data->maxVal.height()); - - if (data->val.width() > data->maxVal.width()) - data->val.setWidth(data->maxVal.width()); - if (data->val.height() > data->maxVal.height()) - data->val.setHeight(data->maxVal.height()); -} - -template -static SizeValue qBoundSize(const SizeValue &minVal, const SizeValue &val, const SizeValue &maxVal) -{ - SizeValue croppedVal = val; - if (minVal.width() > val.width()) - croppedVal.setWidth(minVal.width()); - else if (maxVal.width() < val.width()) - croppedVal.setWidth(maxVal.width()); - - if (minVal.height() > val.height()) - croppedVal.setHeight(minVal.height()); - else if (maxVal.height() < val.height()) - croppedVal.setHeight(maxVal.height()); - - return croppedVal; -} - -// Match the exact signature of qBound for VS 6. -QSize qBound(QSize minVal, QSize val, QSize maxVal) -{ - return qBoundSize(minVal, val, maxVal); -} - -QSizeF qBound(QSizeF minVal, QSizeF val, QSizeF maxVal) -{ - return qBoundSize(minVal, val, maxVal); -} - -namespace { - -namespace { -template -void orderBorders(Value &minVal, Value &maxVal) -{ - if (minVal > maxVal) - qSwap(minVal, maxVal); -} - -template -static void orderSizeBorders(Value &minVal, Value &maxVal) -{ - Value fromSize = minVal; - Value toSize = maxVal; - if (fromSize.width() > toSize.width()) { - fromSize.setWidth(maxVal.width()); - toSize.setWidth(minVal.width()); - } - if (fromSize.height() > toSize.height()) { - fromSize.setHeight(maxVal.height()); - toSize.setHeight(minVal.height()); - } - minVal = fromSize; - maxVal = toSize; -} - -void orderBorders(QSize &minVal, QSize &maxVal) -{ - orderSizeBorders(minVal, maxVal); -} - -void orderBorders(QSizeF &minVal, QSizeF &maxVal) -{ - orderSizeBorders(minVal, maxVal); -} - -} -} -//////// - -template -static Value getData(const QMap &propertyMap, - Value PrivateData::*data, - const QtProperty *property, const Value &defaultValue = Value()) -{ - typedef QMap PropertyToData; - typedef typename PropertyToData::const_iterator PropertyToDataConstIterator; - const PropertyToDataConstIterator it = propertyMap.constFind(property); - if (it == propertyMap.constEnd()) - return defaultValue; - return it.value().*data; -} - -template -static Value getValue(const QMap &propertyMap, - const QtProperty *property, const Value &defaultValue = Value()) -{ - return getData(propertyMap, &PrivateData::val, property, defaultValue); -} - -template -static Value getMinimum(const QMap &propertyMap, - const QtProperty *property, const Value &defaultValue = Value()) -{ - return getData(propertyMap, &PrivateData::minVal, property, defaultValue); -} - -template -static Value getMaximum(const QMap &propertyMap, - const QtProperty *property, const Value &defaultValue = Value()) -{ - return getData(propertyMap, &PrivateData::maxVal, property, defaultValue); -} - -template -static void setSimpleValue(QMap &propertyMap, - PropertyManager *manager, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - QtProperty *property, const Value &val) -{ - typedef QMap PropertyToData; - typedef typename PropertyToData::iterator PropertyToDataIterator; - const PropertyToDataIterator it = propertyMap.find(property); - if (it == propertyMap.end()) - return; - - if (it.value() == val) - return; - - it.value() = val; - - emit (manager->*propertyChangedSignal)(property); - emit (manager->*valueChangedSignal)(property, val); -} - -template -static void setValueInRange(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - QtProperty *property, const Value &val, - void (PropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, ValueChangeParameter)) -{ - typedef typename PropertyManagerPrivate::Data PrivateData; - typedef QMap PropertyToData; - typedef typename PropertyToData::iterator PropertyToDataIterator; - const PropertyToDataIterator it = managerPrivate->m_values.find(property); - if (it == managerPrivate->m_values.end()) - return; - - PrivateData &data = it.value(); - - if (data.val == val) - return; - - const Value oldVal = data.val; - - data.val = qBound(data.minVal, val, data.maxVal); - - if (data.val == oldVal) - return; - - if (setSubPropertyValue) - (managerPrivate->*setSubPropertyValue)(property, data.val); - - emit (manager->*propertyChangedSignal)(property); - emit (manager->*valueChangedSignal)(property, data.val); -} - -template -static void setBorderValues(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), - QtProperty *property, const Value &minVal, const Value &maxVal, - void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, - ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) -{ - typedef typename PropertyManagerPrivate::Data PrivateData; - typedef QMap PropertyToData; - typedef typename PropertyToData::iterator PropertyToDataIterator; - const PropertyToDataIterator it = managerPrivate->m_values.find(property); - if (it == managerPrivate->m_values.end()) - return; - - Value fromVal = minVal; - Value toVal = maxVal; - orderBorders(fromVal, toVal); - - PrivateData &data = it.value(); - - if (data.minVal == fromVal && data.maxVal == toVal) - return; - - const Value oldVal = data.val; - - data.setMinimumValue(fromVal); - data.setMaximumValue(toVal); - - emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); - - if (setSubPropertyRange) - (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); - - if (data.val == oldVal) - return; - - emit (manager->*propertyChangedSignal)(property); - emit (manager->*valueChangedSignal)(property, data.val); -} - -template -static void setBorderValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), - QtProperty *property, - Value (PrivateData::*getRangeVal)() const, - void (PrivateData::*setRangeVal)(ValueChangeParameter), const Value &borderVal, - void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, - ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) -{ - typedef QMap PropertyToData; - typedef typename PropertyToData::iterator PropertyToDataIterator; - const PropertyToDataIterator it = managerPrivate->m_values.find(property); - if (it == managerPrivate->m_values.end()) - return; - - PrivateData &data = it.value(); - - if ((data.*getRangeVal)() == borderVal) - return; - - const Value oldVal = data.val; - - (data.*setRangeVal)(borderVal); - - emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); - - if (setSubPropertyRange) - (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); - - if (data.val == oldVal) - return; - - emit (manager->*propertyChangedSignal)(property); - emit (manager->*valueChangedSignal)(property, data.val); -} - -template -static void setMinimumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), - QtProperty *property, const Value &minVal) -{ - void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, - ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; - setBorderValue(manager, managerPrivate, - propertyChangedSignal, valueChangedSignal, rangeChangedSignal, - property, &PropertyManagerPrivate::Data::minimumValue, &PropertyManagerPrivate::Data::setMinimumValue, minVal, setSubPropertyRange); -} - -template -static void setMaximumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, - void (PropertyManager::*propertyChangedSignal)(QtProperty *), - void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), - void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), - QtProperty *property, const Value &maxVal) -{ - void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, - ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; - setBorderValue(manager, managerPrivate, - propertyChangedSignal, valueChangedSignal, rangeChangedSignal, - property, &PropertyManagerPrivate::Data::maximumValue, &PropertyManagerPrivate::Data::setMaximumValue, maxVal, setSubPropertyRange); -} - -class QtMetaEnumWrapper : public QObject -{ - Q_OBJECT - Q_PROPERTY(QSizePolicy::Policy policy READ policy) -public: - QSizePolicy::Policy policy() const { return QSizePolicy::Ignored; } -private: - explicit QtMetaEnumWrapper(QObject *parent) : QObject(parent) {} -}; - -// QtGroupPropertyManager - -/*! - \class QtGroupPropertyManager - - \brief The QtGroupPropertyManager provides and manages group properties. - - This class is intended to provide a grouping element without any value. - - \sa QtAbstractPropertyManager -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtGroupPropertyManager::QtGroupPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtGroupPropertyManager::~QtGroupPropertyManager() -{ - -} - -/*! - \reimp -*/ -bool QtGroupPropertyManager::hasValue(const QtProperty *property) const -{ - Q_UNUSED(property) - return false; -} - -/*! - \reimp -*/ -void QtGroupPropertyManager::initializeProperty(QtProperty *property) -{ - Q_UNUSED(property) -} - -/*! - \reimp -*/ -void QtGroupPropertyManager::uninitializeProperty(QtProperty *property) -{ - Q_UNUSED(property) -} - -// QtIntPropertyManager - -class QtIntPropertyManagerPrivate -{ - QtIntPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtIntPropertyManager) -public: - - struct Data - { - Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1), readOnly(false) {} - int val; - int minVal; - int maxVal; - int singleStep; - bool readOnly; - int minimumValue() const { return minVal; } - int maximumValue() const { return maxVal; } - void setMinimumValue(int newMinVal) { setSimpleMinimumData(this, newMinVal); } - void setMaximumValue(int newMaxVal) { setSimpleMaximumData(this, newMaxVal); } - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! - \class QtIntPropertyManager - - \brief The QtIntPropertyManager provides and manages int properties. - - An int property has a current value, and a range specifying the - valid values. The range is defined by a minimum and a maximum - value. - - The property's value and range can be retrieved using the value(), - minimum() and maximum() functions, and can be set using the - setValue(), setMinimum() and setMaximum() slots. Alternatively, - the range can be defined in one go using the setRange() slot. - - In addition, QtIntPropertyManager provides the valueChanged() signal which - is emitted whenever a property created by this manager changes, - and the rangeChanged() signal which is emitted whenever such a - property changes its range of valid values. - - \sa QtAbstractPropertyManager, QtSpinBoxFactory, QtSliderFactory, QtScrollBarFactory -*/ - -/*! - \fn void QtIntPropertyManager::valueChanged(QtProperty *property, int value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtIntPropertyManager::rangeChanged(QtProperty *property, int minimum, int maximum) - - This signal is emitted whenever a property created by this manager - changes its range of valid values, passing a pointer to the - \a property and the new \a minimum and \a maximum values. - - \sa setRange() -*/ - -/*! - \fn void QtIntPropertyManager::singleStepChanged(QtProperty *property, int step) - - This signal is emitted whenever a property created by this manager - changes its single step property, passing a pointer to the - \a property and the new \a step value - - \sa setSingleStep() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtIntPropertyManager::QtIntPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtIntPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtIntPropertyManager::~QtIntPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns 0. - - \sa setValue() -*/ -int QtIntPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property, 0); -} - -/*! - Returns the given \a property's minimum value. - - \sa setMinimum(), maximum(), setRange() -*/ -int QtIntPropertyManager::minimum(const QtProperty *property) const -{ - return getMinimum(d_ptr->m_values, property, 0); -} - -/*! - Returns the given \a property's maximum value. - - \sa setMaximum(), minimum(), setRange() -*/ -int QtIntPropertyManager::maximum(const QtProperty *property) const -{ - return getMaximum(d_ptr->m_values, property, 0); -} - -/*! - Returns the given \a property's step value. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa setSingleStep() -*/ -int QtIntPropertyManager::singleStep(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtIntPropertyManagerPrivate::Data::singleStep, property, 0); -} - -/*! - Returns read-only status of the property. - - When property is read-only it's value can be selected and copied from editor but not modified. - - \sa QtIntPropertyManager::setReadOnly -*/ -bool QtIntPropertyManager::isReadOnly(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtIntPropertyManagerPrivate::Data::readOnly, property, false); -} - -/*! - \reimp -*/ -QString QtIntPropertyManager::valueText(const QtProperty *property) const -{ - const QtIntPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - return QString::number(it.value().val); -} - -/*! - \fn void QtIntPropertyManager::setValue(QtProperty *property, int value) - - Sets the value of the given \a property to \a value. - - If the specified \a value is not valid according to the given \a - property's range, the \a value is adjusted to the nearest valid - value within the range. - - \sa value(), setRange(), valueChanged() -*/ -void QtIntPropertyManager::setValue(QtProperty *property, int val) -{ - void (QtIntPropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, int) = 0; - setValueInRange(this, d_ptr, - &QtIntPropertyManager::propertyChanged, - &QtIntPropertyManager::valueChanged, - property, val, setSubPropertyValue); -} - -/*! - Sets the minimum value for the given \a property to \a minVal. - - When setting the minimum value, the maximum and current values are - adjusted if necessary (ensuring that the range remains valid and - that the current value is within the range). - - \sa minimum(), setRange(), rangeChanged() -*/ -void QtIntPropertyManager::setMinimum(QtProperty *property, int minVal) -{ - setMinimumValue(this, d_ptr, - &QtIntPropertyManager::propertyChanged, - &QtIntPropertyManager::valueChanged, - &QtIntPropertyManager::rangeChanged, - property, minVal); -} - -/*! - Sets the maximum value for the given \a property to \a maxVal. - - When setting maximum value, the minimum and current values are - adjusted if necessary (ensuring that the range remains valid and - that the current value is within the range). - - \sa maximum(), setRange(), rangeChanged() -*/ -void QtIntPropertyManager::setMaximum(QtProperty *property, int maxVal) -{ - setMaximumValue(this, d_ptr, - &QtIntPropertyManager::propertyChanged, - &QtIntPropertyManager::valueChanged, - &QtIntPropertyManager::rangeChanged, - property, maxVal); -} - -/*! - \fn void QtIntPropertyManager::setRange(QtProperty *property, int minimum, int maximum) - - Sets the range of valid values. - - This is a convenience function defining the range of valid values - in one go; setting the \a minimum and \a maximum values for the - given \a property with a single function call. - - When setting a new range, the current value is adjusted if - necessary (ensuring that the value remains within range). - - \sa setMinimum(), setMaximum(), rangeChanged() -*/ -void QtIntPropertyManager::setRange(QtProperty *property, int minVal, int maxVal) -{ - void (QtIntPropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, int, int, int) = 0; - setBorderValues(this, d_ptr, - &QtIntPropertyManager::propertyChanged, - &QtIntPropertyManager::valueChanged, - &QtIntPropertyManager::rangeChanged, - property, minVal, maxVal, setSubPropertyRange); -} - -/*! - Sets the step value for the given \a property to \a step. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa singleStep() -*/ -void QtIntPropertyManager::setSingleStep(QtProperty *property, int step) -{ - const QtIntPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtIntPropertyManagerPrivate::Data data = it.value(); - - if (step < 0) - step = 0; - - if (data.singleStep == step) - return; - - data.singleStep = step; - - it.value() = data; - - emit singleStepChanged(property, data.singleStep); -} - -/*! - Sets read-only status of the property. - - \sa QtIntPropertyManager::setReadOnly -*/ -void QtIntPropertyManager::setReadOnly(QtProperty *property, bool readOnly) -{ - const QtIntPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtIntPropertyManagerPrivate::Data data = it.value(); - - if (data.readOnly == readOnly) - return; - - data.readOnly = readOnly; - it.value() = data; - - emit propertyChanged(property); - emit readOnlyChanged(property, data.readOnly); -} - -/*! - \reimp -*/ -void QtIntPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtIntPropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtIntPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtDoublePropertyManager - -class QtDoublePropertyManagerPrivate -{ - QtDoublePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtDoublePropertyManager) -public: - - struct Data - { - Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1), decimals(2), readOnly(false) {} - double val; - double minVal; - double maxVal; - double singleStep; - int decimals; - bool readOnly; - double minimumValue() const { return minVal; } - double maximumValue() const { return maxVal; } - void setMinimumValue(double newMinVal) { setSimpleMinimumData(this, newMinVal); } - void setMaximumValue(double newMaxVal) { setSimpleMaximumData(this, newMaxVal); } - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! - \class QtDoublePropertyManager - - \brief The QtDoublePropertyManager provides and manages double properties. - - A double property has a current value, and a range specifying the - valid values. The range is defined by a minimum and a maximum - value. - - The property's value and range can be retrieved using the value(), - minimum() and maximum() functions, and can be set using the - setValue(), setMinimum() and setMaximum() slots. - Alternatively, the range can be defined in one go using the - setRange() slot. - - In addition, QtDoublePropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the rangeChanged() signal which is emitted whenever - such a property changes its range of valid values. - - \sa QtAbstractPropertyManager, QtDoubleSpinBoxFactory -*/ - -/*! - \fn void QtDoublePropertyManager::valueChanged(QtProperty *property, double value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtDoublePropertyManager::rangeChanged(QtProperty *property, double minimum, double maximum) - - This signal is emitted whenever a property created by this manager - changes its range of valid values, passing a pointer to the - \a property and the new \a minimum and \a maximum values - - \sa setRange() -*/ - -/*! - \fn void QtDoublePropertyManager::decimalsChanged(QtProperty *property, int prec) - - This signal is emitted whenever a property created by this manager - changes its precision of value, passing a pointer to the - \a property and the new \a prec value - - \sa setDecimals() -*/ - -/*! - \fn void QtDoublePropertyManager::singleStepChanged(QtProperty *property, double step) - - This signal is emitted whenever a property created by this manager - changes its single step property, passing a pointer to the - \a property and the new \a step value - - \sa setSingleStep() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtDoublePropertyManager::QtDoublePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtDoublePropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtDoublePropertyManager::~QtDoublePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns 0. - - \sa setValue() -*/ -double QtDoublePropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property, 0.0); -} - -/*! - Returns the given \a property's minimum value. - - \sa maximum(), setRange() -*/ -double QtDoublePropertyManager::minimum(const QtProperty *property) const -{ - return getMinimum(d_ptr->m_values, property, 0.0); -} - -/*! - Returns the given \a property's maximum value. - - \sa minimum(), setRange() -*/ -double QtDoublePropertyManager::maximum(const QtProperty *property) const -{ - return getMaximum(d_ptr->m_values, property, 0.0); -} - -/*! - Returns the given \a property's step value. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa setSingleStep() -*/ -double QtDoublePropertyManager::singleStep(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::singleStep, property, 0); -} - -/*! - Returns the given \a property's precision, in decimals. - - \sa setDecimals() -*/ -int QtDoublePropertyManager::decimals(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::decimals, property, 0); -} - -/*! - Returns read-only status of the property. - - When property is read-only it's value can be selected and copied from editor but not modified. - - \sa QtDoublePropertyManager::setReadOnly -*/ -bool QtDoublePropertyManager::isReadOnly(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::readOnly, property, false); -} - -/*! - \reimp -*/ -QString QtDoublePropertyManager::valueText(const QtProperty *property) const -{ - const QtDoublePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const int decimals = it.value().decimals; - const QString text = QLocale::system().toString(it.value().val, 'f', decimals); - - // remove redundant trailing 0's in case of high precision - if (decimals > 3) - return removeRedundantTrialingZeros(text); - - return text; -} - -/*! - \fn void QtDoublePropertyManager::setValue(QtProperty *property, double value) - - Sets the value of the given \a property to \a value. - - If the specified \a value is not valid according to the given - \a property's range, the \a value is adjusted to the nearest valid value - within the range. - - \sa value(), setRange(), valueChanged() -*/ -void QtDoublePropertyManager::setValue(QtProperty *property, double val) -{ - void (QtDoublePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, double) = 0; - setValueInRange(this, d_ptr, - &QtDoublePropertyManager::propertyChanged, - &QtDoublePropertyManager::valueChanged, - property, val, setSubPropertyValue); -} - -/*! - Sets the step value for the given \a property to \a step. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa singleStep() -*/ -void QtDoublePropertyManager::setSingleStep(QtProperty *property, double step) -{ - const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtDoublePropertyManagerPrivate::Data data = it.value(); - - if (step < 0) - step = 0; - - if (data.singleStep == step) - return; - - data.singleStep = step; - - it.value() = data; - - emit singleStepChanged(property, data.singleStep); -} - -/*! - Sets read-only status of the property. - - \sa QtDoublePropertyManager::setReadOnly -*/ -void QtDoublePropertyManager::setReadOnly(QtProperty *property, bool readOnly) -{ - const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtDoublePropertyManagerPrivate::Data data = it.value(); - - if (data.readOnly == readOnly) - return; - - data.readOnly = readOnly; - it.value() = data; - - emit propertyChanged(property); - emit readOnlyChanged(property, data.readOnly); -} - -/*! - \fn void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) - - Sets the precision of the given \a property to \a prec. - - The valid decimal range is 0-13. The default is 2. - - \sa decimals() -*/ -void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) -{ - const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtDoublePropertyManagerPrivate::Data data = it.value(); - - if (prec > 13) - prec = 13; - else if (prec < 0) - prec = 0; - - if (data.decimals == prec) - return; - - data.decimals = prec; - - it.value() = data; - - emit decimalsChanged(property, data.decimals); -} - -/*! - Sets the minimum value for the given \a property to \a minVal. - - When setting the minimum value, the maximum and current values are - adjusted if necessary (ensuring that the range remains valid and - that the current value is within in the range). - - \sa minimum(), setRange(), rangeChanged() -*/ -void QtDoublePropertyManager::setMinimum(QtProperty *property, double minVal) -{ - setMinimumValue(this, d_ptr, - &QtDoublePropertyManager::propertyChanged, - &QtDoublePropertyManager::valueChanged, - &QtDoublePropertyManager::rangeChanged, - property, minVal); -} - -/*! - Sets the maximum value for the given \a property to \a maxVal. - - When setting the maximum value, the minimum and current values are - adjusted if necessary (ensuring that the range remains valid and - that the current value is within in the range). - - \sa maximum(), setRange(), rangeChanged() -*/ -void QtDoublePropertyManager::setMaximum(QtProperty *property, double maxVal) -{ - setMaximumValue(this, d_ptr, - &QtDoublePropertyManager::propertyChanged, - &QtDoublePropertyManager::valueChanged, - &QtDoublePropertyManager::rangeChanged, - property, maxVal); -} - -/*! - \fn void QtDoublePropertyManager::setRange(QtProperty *property, double minimum, double maximum) - - Sets the range of valid values. - - This is a convenience function defining the range of valid values - in one go; setting the \a minimum and \a maximum values for the - given \a property with a single function call. - - When setting a new range, the current value is adjusted if - necessary (ensuring that the value remains within range). - - \sa setMinimum(), setMaximum(), rangeChanged() -*/ -void QtDoublePropertyManager::setRange(QtProperty *property, double minVal, double maxVal) -{ - void (QtDoublePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, double, double, double) = 0; - setBorderValues(this, d_ptr, - &QtDoublePropertyManager::propertyChanged, - &QtDoublePropertyManager::valueChanged, - &QtDoublePropertyManager::rangeChanged, - property, minVal, maxVal, setSubPropertyRange); -} - -/*! - \reimp -*/ -void QtDoublePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtDoublePropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtDoublePropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtStringPropertyManager - -class QtStringPropertyManagerPrivate -{ - QtStringPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtStringPropertyManager) -public: - - struct Data - { - Data() : - regExp(QStringLiteral(".*"), QRegularExpression::CaseInsensitiveOption), - echoMode(QLineEdit::Normal), - readOnly(false) - { - } - QString val; - QRegularExpression regExp; - int echoMode; - bool readOnly; - }; - - typedef QMap PropertyValueMap; - QMap m_values; -}; - -/*! - \class QtStringPropertyManager - - \brief The QtStringPropertyManager provides and manages QString properties. - - A string property's value can be retrieved using the value() - function, and set using the setValue() slot. - - The current value can be checked against a regular expression. To - set the regular expression use the setRegExp() slot, use the - regExp() function to retrieve the currently set expression. - - In addition, QtStringPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the regExpChanged() signal which is emitted whenever - such a property changes its currently set regular expression. - - \sa QtAbstractPropertyManager, QtLineEditFactory -*/ - -/*! - \fn void QtStringPropertyManager::valueChanged(QtProperty *property, const QString &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtStringPropertyManager::regExpChanged(QtProperty *property, const QRegularExpression ®Exp) - - This signal is emitted whenever a property created by this manager - changes its currenlty set regular expression, passing a pointer to - the \a property and the new \a regExp as parameters. - - \sa setRegExp() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtStringPropertyManager::QtStringPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtStringPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtStringPropertyManager::~QtStringPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns an empty string. - - \sa setValue() -*/ -QString QtStringPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's currently set regular expression. - - If the given \a property is not managed by this manager, this - function returns an empty expression. - - \sa setRegExp() -*/ -QRegularExpression QtStringPropertyManager::regExp(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtStringPropertyManagerPrivate::Data::regExp, property, QRegularExpression()); -} - -/*! - \reimp -*/ -EchoMode QtStringPropertyManager::echoMode(const QtProperty *property) const -{ - return (EchoMode)getData(d_ptr->m_values, &QtStringPropertyManagerPrivate::Data::echoMode, property, 0); -} - -/*! - Returns read-only status of the property. - - When property is read-only it's value can be selected and copied from editor but not modified. - - \sa QtStringPropertyManager::setReadOnly -*/ -bool QtStringPropertyManager::isReadOnly(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtStringPropertyManagerPrivate::Data::readOnly, property, false); -} - -/*! - \reimp -*/ -QString QtStringPropertyManager::valueText(const QtProperty *property) const -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - return it.value().val; -} - -/*! - \reimp -*/ -QString QtStringPropertyManager::displayText(const QtProperty *property) const -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - QLineEdit edit; - edit.setEchoMode((EchoMode)it.value().echoMode); - edit.setText(it.value().val); - return edit.displayText(); -} - -/*! - \fn void QtStringPropertyManager::setValue(QtProperty *property, const QString &value) - - Sets the value of the given \a property to \a value. - - If the specified \a value doesn't match the given \a property's - regular expression, this function does nothing. - - \sa value(), setRegExp(), valueChanged() -*/ -void QtStringPropertyManager::setValue(QtProperty *property, const QString &val) -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtStringPropertyManagerPrivate::Data data = it.value(); - - if (data.val == val) - return; - - if (data.regExp.isValid() && !data.regExp.match(val).hasMatch()) - return; - - data.val = val; - - it.value() = data; - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the regular expression of the given \a property to \a regExp. - - \sa regExp(), setValue(), regExpChanged() -*/ -void QtStringPropertyManager::setRegExp(QtProperty *property, const QRegularExpression ®Exp) -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtStringPropertyManagerPrivate::Data data = it.value() ; - - if (data.regExp == regExp) - return; - - data.regExp = regExp; - - it.value() = data; - - emit regExpChanged(property, data.regExp); -} - - -void QtStringPropertyManager::setEchoMode(QtProperty *property, EchoMode echoMode) -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtStringPropertyManagerPrivate::Data data = it.value(); - - if (data.echoMode == echoMode) - return; - - data.echoMode = echoMode; - it.value() = data; - - emit propertyChanged(property); - emit echoModeChanged(property, data.echoMode); -} - -/*! - Sets read-only status of the property. - - \sa QtStringPropertyManager::setReadOnly -*/ -void QtStringPropertyManager::setReadOnly(QtProperty *property, bool readOnly) -{ - const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtStringPropertyManagerPrivate::Data data = it.value(); - - if (data.readOnly == readOnly) - return; - - data.readOnly = readOnly; - it.value() = data; - - emit propertyChanged(property); - emit echoModeChanged(property, data.echoMode); -} - -/*! - \reimp -*/ -void QtStringPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtStringPropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtStringPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtBoolPropertyManager -// Return an icon containing a check box indicator -static QIcon drawCheckBox(bool value) -{ - QStyleOptionButton opt; - opt.state |= value ? QStyle::State_On : QStyle::State_Off; - opt.state |= QStyle::State_Enabled; - const QStyle *style = QApplication::style(); - // Figure out size of an indicator and make sure it is not scaled down in a list view item - // by making the pixmap as big as a list view icon and centering the indicator in it. - // (if it is smaller, it can't be helped) - const int indicatorWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &opt); - const int indicatorHeight = style->pixelMetric(QStyle::PM_IndicatorHeight, &opt); - const int listViewIconSize = indicatorWidth; - const int pixmapWidth = indicatorWidth; - const int pixmapHeight = qMax(indicatorHeight, listViewIconSize); - - opt.rect = QRect(0, 0, indicatorWidth, indicatorHeight); - QPixmap pixmap = QPixmap(pixmapWidth, pixmapHeight); - pixmap.fill(Qt::transparent); - { - // Center? - const int xoff = (pixmapWidth > indicatorWidth) ? (pixmapWidth - indicatorWidth) / 2 : 0; - const int yoff = (pixmapHeight > indicatorHeight) ? (pixmapHeight - indicatorHeight) / 2 : 0; - QPainter painter(&pixmap); - painter.translate(xoff, yoff); - style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &painter); - } - return QIcon(pixmap); -} - -class QtBoolPropertyManagerPrivate -{ - QtBoolPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtBoolPropertyManager) -public: - QtBoolPropertyManagerPrivate(); - - struct Data - { - Data() : val(false), textVisible(true) {} - bool val; - bool textVisible; - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - static QIcon m_checkedIcon; - static QIcon m_uncheckedIcon; -}; - -QIcon QtBoolPropertyManagerPrivate::m_checkedIcon; -QIcon QtBoolPropertyManagerPrivate::m_uncheckedIcon; - -QtBoolPropertyManagerPrivate::QtBoolPropertyManagerPrivate() -{ -} - -/*! - \class QtBoolPropertyManager - - \brief The QtBoolPropertyManager class provides and manages boolean properties. - - The property's value can be retrieved using the value() function, - and set using the setValue() slot. - - In addition, QtBoolPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager, QtCheckBoxFactory -*/ - -/*! - \fn void QtBoolPropertyManager::valueChanged(QtProperty *property, bool value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtBoolPropertyManager::QtBoolPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtBoolPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtBoolPropertyManager::~QtBoolPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by \e this manager, this - function returns false. - - \sa setValue() -*/ -bool QtBoolPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property, false); -} - -bool QtBoolPropertyManager::textVisible(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtBoolPropertyManagerPrivate::Data::textVisible, property, false); -} - -/*! - Resets the icons used to draw the checked and unchecked states. Should be - called after theme changes. -*/ -void QtBoolPropertyManager::resetIcons() -{ - QtBoolPropertyManagerPrivate::m_checkedIcon = QIcon(); - QtBoolPropertyManagerPrivate::m_uncheckedIcon = QIcon(); -} - -/*! - \reimp -*/ -QString QtBoolPropertyManager::valueText(const QtProperty *property) const -{ - const QtBoolPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - const QtBoolPropertyManagerPrivate::Data &data = it.value(); - if (!data.textVisible) - return QString(); - - return data.val ? tr("True") : tr("False"); -} - -/*! - \reimp -*/ -QIcon QtBoolPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtBoolPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QIcon(); - - if (QtBoolPropertyManagerPrivate::m_checkedIcon.isNull()) { - QtBoolPropertyManagerPrivate::m_checkedIcon = drawCheckBox(true); - QtBoolPropertyManagerPrivate::m_uncheckedIcon = drawCheckBox(false); - } - - return it.value().val ? d_ptr->m_checkedIcon : d_ptr->m_uncheckedIcon; -} - -/*! - \fn void QtBoolPropertyManager::setValue(QtProperty *property, bool value) - - Sets the value of the given \a property to \a value. - - \sa value() -*/ -void QtBoolPropertyManager::setValue(QtProperty *property, bool val) -{ - const QtBoolPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtBoolPropertyManagerPrivate::Data data = it.value(); - - if (data.val == val) - return; - - data.val = val; - it.value() = data; - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -void QtBoolPropertyManager::setTextVisible(QtProperty *property, bool textVisible) -{ - const QtBoolPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtBoolPropertyManagerPrivate::Data data = it.value(); - - if (data.textVisible == textVisible) - return; - - data.textVisible = textVisible; - it.value() = data; - - emit propertyChanged(property); - emit textVisibleChanged(property, data.textVisible); -} - -/*! - \reimp -*/ -void QtBoolPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtBoolPropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtBoolPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtDatePropertyManager - -class QtDatePropertyManagerPrivate -{ - QtDatePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtDatePropertyManager) -public: - - struct Data - { - Data() : val(QDate::currentDate()), minVal(QDate(1752, 9, 14)), - maxVal(QDate(7999, 12, 31)) {} - QDate val; - QDate minVal; - QDate maxVal; - QDate minimumValue() const { return minVal; } - QDate maximumValue() const { return maxVal; } - void setMinimumValue(const QDate &newMinVal) { setSimpleMinimumData(this, newMinVal); } - void setMaximumValue(const QDate &newMaxVal) { setSimpleMaximumData(this, newMaxVal); } - }; - - QString m_format; - - typedef QMap PropertyValueMap; - QMap m_values; -}; - -/*! - \class QtDatePropertyManager - - \brief The QtDatePropertyManager provides and manages QDate properties. - - A date property has a current value, and a range specifying the - valid dates. The range is defined by a minimum and a maximum - value. - - The property's values can be retrieved using the minimum(), - maximum() and value() functions, and can be set using the - setMinimum(), setMaximum() and setValue() slots. Alternatively, - the range can be defined in one go using the setRange() slot. - - In addition, QtDatePropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the rangeChanged() signal which is emitted whenever - such a property changes its range of valid dates. - - \sa QtAbstractPropertyManager, QtDateEditFactory, QtDateTimePropertyManager -*/ - -/*! - \fn void QtDatePropertyManager::valueChanged(QtProperty *property, const QDate &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtDatePropertyManager::rangeChanged(QtProperty *property, const QDate &minimum, const QDate &maximum) - - This signal is emitted whenever a property created by this manager - changes its range of valid dates, passing a pointer to the \a - property and the new \a minimum and \a maximum dates. - - \sa setRange() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtDatePropertyManager::QtDatePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtDatePropertyManagerPrivate; - d_ptr->q_ptr = this; - - QLocale loc; - d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtDatePropertyManager::~QtDatePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by \e this manager, this - function returns an invalid date. - - \sa setValue() -*/ -QDate QtDatePropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's minimum date. - - \sa maximum(), setRange() -*/ -QDate QtDatePropertyManager::minimum(const QtProperty *property) const -{ - return getMinimum(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's maximum date. - - \sa minimum(), setRange() -*/ -QDate QtDatePropertyManager::maximum(const QtProperty *property) const -{ - return getMaximum(d_ptr->m_values, property); -} - -/*! - \reimp -*/ -QString QtDatePropertyManager::valueText(const QtProperty *property) const -{ - const QtDatePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - return it.value().val.toString(d_ptr->m_format); -} - -/*! - \fn void QtDatePropertyManager::setValue(QtProperty *property, const QDate &value) - - Sets the value of the given \a property to \a value. - - If the specified \a value is not a valid date according to the - given \a property's range, the value is adjusted to the nearest - valid value within the range. - - \sa value(), setRange(), valueChanged() -*/ -void QtDatePropertyManager::setValue(QtProperty *property, const QDate &val) -{ - void (QtDatePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, const QDate &) = 0; - setValueInRange(this, d_ptr, - &QtDatePropertyManager::propertyChanged, - &QtDatePropertyManager::valueChanged, - property, val, setSubPropertyValue); -} - -/*! - Sets the minimum value for the given \a property to \a minVal. - - When setting the minimum value, the maximum and current values are - adjusted if necessary (ensuring that the range remains valid and - that the current value is within in the range). - - \sa minimum(), setRange() -*/ -void QtDatePropertyManager::setMinimum(QtProperty *property, const QDate &minVal) -{ - setMinimumValue(this, d_ptr, - &QtDatePropertyManager::propertyChanged, - &QtDatePropertyManager::valueChanged, - &QtDatePropertyManager::rangeChanged, - property, minVal); -} - -/*! - Sets the maximum value for the given \a property to \a maxVal. - - When setting the maximum value, the minimum and current - values are adjusted if necessary (ensuring that the range remains - valid and that the current value is within in the range). - - \sa maximum(), setRange() -*/ -void QtDatePropertyManager::setMaximum(QtProperty *property, const QDate &maxVal) -{ - setMaximumValue(this, d_ptr, - &QtDatePropertyManager::propertyChanged, - &QtDatePropertyManager::valueChanged, - &QtDatePropertyManager::rangeChanged, - property, maxVal); -} - -/*! - \fn void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minimum, const QDate &maximum) - - Sets the range of valid dates. - - This is a convenience function defining the range of valid dates - in one go; setting the \a minimum and \a maximum values for the - given \a property with a single function call. - - When setting a new date range, the current value is adjusted if - necessary (ensuring that the value remains in date range). - - \sa setMinimum(), setMaximum(), rangeChanged() -*/ -void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal) -{ - void (QtDatePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, const QDate &, - const QDate &, const QDate &) = 0; - setBorderValues(this, d_ptr, - &QtDatePropertyManager::propertyChanged, - &QtDatePropertyManager::valueChanged, - &QtDatePropertyManager::rangeChanged, - property, minVal, maxVal, setSubPropertyRange); -} - -/*! - \reimp -*/ -void QtDatePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtDatePropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtDatePropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtTimePropertyManager - -class QtTimePropertyManagerPrivate -{ - QtTimePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtTimePropertyManager) -public: - - QString m_format; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! - \class QtTimePropertyManager - - \brief The QtTimePropertyManager provides and manages QTime properties. - - A time property's value can be retrieved using the value() - function, and set using the setValue() slot. - - In addition, QtTimePropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager, QtTimeEditFactory -*/ - -/*! - \fn void QtTimePropertyManager::valueChanged(QtProperty *property, const QTime &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtTimePropertyManager::QtTimePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtTimePropertyManagerPrivate; - d_ptr->q_ptr = this; - - QLocale loc; - d_ptr->m_format = loc.timeFormat(QLocale::ShortFormat); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtTimePropertyManager::~QtTimePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns an invalid time object. - - \sa setValue() -*/ -QTime QtTimePropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QTime()); -} - -/*! - \reimp -*/ -QString QtTimePropertyManager::valueText(const QtProperty *property) const -{ - const QtTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - return it.value().toString(d_ptr->m_format); -} - -/*! - \fn void QtTimePropertyManager::setValue(QtProperty *property, const QTime &value) - - Sets the value of the given \a property to \a value. - - \sa value(), valueChanged() -*/ -void QtTimePropertyManager::setValue(QtProperty *property, const QTime &val) -{ - setSimpleValue(d_ptr->m_values, this, - &QtTimePropertyManager::propertyChanged, - &QtTimePropertyManager::valueChanged, - property, val); -} - -/*! - \reimp -*/ -void QtTimePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QTime::currentTime(); -} - -/*! - \reimp -*/ -void QtTimePropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtDateTimePropertyManager - -class QtDateTimePropertyManagerPrivate -{ - QtDateTimePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtDateTimePropertyManager) -public: - - QString m_format; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! \class QtDateTimePropertyManager - - \brief The QtDateTimePropertyManager provides and manages QDateTime properties. - - A date and time property has a current value which can be - retrieved using the value() function, and set using the setValue() - slot. In addition, QtDateTimePropertyManager provides the - valueChanged() signal which is emitted whenever a property created - by this manager changes. - - \sa QtAbstractPropertyManager, QtDateTimeEditFactory, QtDatePropertyManager -*/ - -/*! - \fn void QtDateTimePropertyManager::valueChanged(QtProperty *property, const QDateTime &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtDateTimePropertyManager::QtDateTimePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtDateTimePropertyManagerPrivate; - d_ptr->q_ptr = this; - - QLocale loc; - d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); - d_ptr->m_format += QLatin1Char(' '); - d_ptr->m_format += loc.timeFormat(QLocale::ShortFormat); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtDateTimePropertyManager::~QtDateTimePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid QDateTime object. - - \sa setValue() -*/ -QDateTime QtDateTimePropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QDateTime()); -} - -/*! - \reimp -*/ -QString QtDateTimePropertyManager::valueText(const QtProperty *property) const -{ - const QtDateTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - return it.value().toString(d_ptr->m_format); -} - -/*! - \fn void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &value) - - Sets the value of the given \a property to \a value. - - \sa value(), valueChanged() -*/ -void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &val) -{ - setSimpleValue(d_ptr->m_values, this, - &QtDateTimePropertyManager::propertyChanged, - &QtDateTimePropertyManager::valueChanged, - property, val); -} - -/*! - \reimp -*/ -void QtDateTimePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QDateTime::currentDateTime(); -} - -/*! - \reimp -*/ -void QtDateTimePropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtKeySequencePropertyManager - -class QtKeySequencePropertyManagerPrivate -{ - QtKeySequencePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtKeySequencePropertyManager) -public: - - QString m_format; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! \class QtKeySequencePropertyManager - - \brief The QtKeySequencePropertyManager provides and manages QKeySequence properties. - - A key sequence's value can be retrieved using the value() - function, and set using the setValue() slot. - - In addition, QtKeySequencePropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager -*/ - -/*! - \fn void QtKeySequencePropertyManager::valueChanged(QtProperty *property, const QKeySequence &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtKeySequencePropertyManager::QtKeySequencePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtKeySequencePropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtKeySequencePropertyManager::~QtKeySequencePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an empty QKeySequence object. - - \sa setValue() -*/ -QKeySequence QtKeySequencePropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QKeySequence()); -} - -/*! - \reimp -*/ -QString QtKeySequencePropertyManager::valueText(const QtProperty *property) const -{ - const QtKeySequencePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - return it.value().toString(QKeySequence::NativeText); -} - -/*! - \fn void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &value) - - Sets the value of the given \a property to \a value. - - \sa value(), valueChanged() -*/ -void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &val) -{ - setSimpleValue(d_ptr->m_values, this, - &QtKeySequencePropertyManager::propertyChanged, - &QtKeySequencePropertyManager::valueChanged, - property, val); -} - -/*! - \reimp -*/ -void QtKeySequencePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QKeySequence(); -} - -/*! - \reimp -*/ -void QtKeySequencePropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtCharPropertyManager - -class QtCharPropertyManagerPrivate -{ - QtCharPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtCharPropertyManager) -public: - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! \class QtCharPropertyManager - - \brief The QtCharPropertyManager provides and manages QChar properties. - - A char's value can be retrieved using the value() - function, and set using the setValue() slot. - - In addition, QtCharPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager -*/ - -/*! - \fn void QtCharPropertyManager::valueChanged(QtProperty *property, const QChar &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtCharPropertyManager::QtCharPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtCharPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtCharPropertyManager::~QtCharPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an null QChar object. - - \sa setValue() -*/ -QChar QtCharPropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QChar()); -} - -/*! - \reimp -*/ -QString QtCharPropertyManager::valueText(const QtProperty *property) const -{ - const QtCharPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QChar c = it.value(); - return c.isNull() ? QString() : QString(c); -} - -/*! - \fn void QtCharPropertyManager::setValue(QtProperty *property, const QChar &value) - - Sets the value of the given \a property to \a value. - - \sa value(), valueChanged() -*/ -void QtCharPropertyManager::setValue(QtProperty *property, const QChar &val) -{ - setSimpleValue(d_ptr->m_values, this, - &QtCharPropertyManager::propertyChanged, - &QtCharPropertyManager::valueChanged, - property, val); -} - -/*! - \reimp -*/ -void QtCharPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QChar(); -} - -/*! - \reimp -*/ -void QtCharPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtPointPropertyManager - -class QtPointPropertyManagerPrivate -{ - QtPointPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtPointPropertyManager) -public: - - void slotIntChanged(QtProperty *property, int value); - void slotPropertyDestroyed(QtProperty *property); - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtIntPropertyManager *m_intPropertyManager; - - QMap m_propertyToX; - QMap m_propertyToY; - - QMap m_xToProperty; - QMap m_yToProperty; -}; - -void QtPointPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) -{ - if (QtProperty *xprop = m_xToProperty.value(property, 0)) { - QPoint p = m_values[xprop]; - p.setX(value); - q_ptr->setValue(xprop, p); - } else if (QtProperty *yprop = m_yToProperty.value(property, 0)) { - QPoint p = m_values[yprop]; - p.setY(value); - q_ptr->setValue(yprop, p); - } -} - -void QtPointPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { - m_propertyToX[pointProp] = 0; - m_xToProperty.remove(property); - } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { - m_propertyToY[pointProp] = 0; - m_yToProperty.remove(property); - } -} - -/*! \class QtPointPropertyManager - - \brief The QtPointPropertyManager provides and manages QPoint properties. - - A point property has nested \e x and \e y subproperties. The - top-level property's value can be retrieved using the value() - function, and set using the setValue() slot. - - The subproperties are created by a QtIntPropertyManager object. This - manager can be retrieved using the subIntPropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - In addition, QtPointPropertyManager provides the valueChanged() signal which - is emitted whenever a property created by this manager changes. - - \sa QtAbstractPropertyManager, QtIntPropertyManager, QtPointFPropertyManager -*/ - -/*! - \fn void QtPointPropertyManager::valueChanged(QtProperty *property, const QPoint &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtPointPropertyManager::QtPointPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtPointPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); - connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotIntChanged(QtProperty *, int))); - connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtPointPropertyManager::~QtPointPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e x and \e y - subproperties. - - In order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtIntPropertyManager *QtPointPropertyManager::subIntPropertyManager() const -{ - return d_ptr->m_intPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns a point with coordinates (0, 0). - - \sa setValue() -*/ -QPoint QtPointPropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QPoint()); -} - -/*! - \reimp -*/ -QString QtPointPropertyManager::valueText(const QtProperty *property) const -{ - const QtPointPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QPoint v = it.value(); - return QString(tr("(%1, %2)").arg(QString::number(v.x())) - .arg(QString::number(v.y()))); -} - -/*! - \fn void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - \sa value(), valueChanged() -*/ -void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &val) -{ - const QtPointPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - if (it.value() == val) - return; - - it.value() = val; - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); - - emit propertyChanged(property); - emit valueChanged(property, val); -} - -/*! - \reimp -*/ -void QtPointPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QPoint(0, 0); - - QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); - xProp->setPropertyName(tr("X")); - d_ptr->m_intPropertyManager->setValue(xProp, 0); - d_ptr->m_propertyToX[property] = xProp; - d_ptr->m_xToProperty[xProp] = property; - property->addSubProperty(xProp); - - QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); - yProp->setPropertyName(tr("Y")); - d_ptr->m_intPropertyManager->setValue(yProp, 0); - d_ptr->m_propertyToY[property] = yProp; - d_ptr->m_yToProperty[yProp] = property; - property->addSubProperty(yProp); -} - -/*! - \reimp -*/ -void QtPointPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *xProp = d_ptr->m_propertyToX[property]; - if (xProp) { - d_ptr->m_xToProperty.remove(xProp); - delete xProp; - } - d_ptr->m_propertyToX.remove(property); - - QtProperty *yProp = d_ptr->m_propertyToY[property]; - if (yProp) { - d_ptr->m_yToProperty.remove(yProp); - delete yProp; - } - d_ptr->m_propertyToY.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtPointFPropertyManager - -class QtPointFPropertyManagerPrivate -{ - QtPointFPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtPointFPropertyManager) -public: - - struct Data - { - QPointF val; - double singleStep = 1.0; - int decimals = 2; - }; - - void slotDoubleChanged(QtProperty *property, double value); - void slotPropertyDestroyed(QtProperty *property); - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtDoublePropertyManager *m_doublePropertyManager; - - QMap m_propertyToX; - QMap m_propertyToY; - - QMap m_xToProperty; - QMap m_yToProperty; -}; - -void QtPointFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) -{ - if (QtProperty *prop = m_xToProperty.value(property, 0)) { - QPointF p = m_values[prop].val; - p.setX(value); - q_ptr->setValue(prop, p); - } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { - QPointF p = m_values[prop].val; - p.setY(value); - q_ptr->setValue(prop, p); - } -} - -void QtPointFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { - m_propertyToX[pointProp] = 0; - m_xToProperty.remove(property); - } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { - m_propertyToY[pointProp] = 0; - m_yToProperty.remove(property); - } -} - -/*! \class QtPointFPropertyManager - - \brief The QtPointFPropertyManager provides and manages QPointF properties. - - A point property has nested \e x and \e y subproperties. The - top-level property's value can be retrieved using the value() - function, and set using the setValue() slot. - - The subproperties are created by a QtDoublePropertyManager object. This - manager can be retrieved using the subDoublePropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - In addition, QtPointFPropertyManager provides the valueChanged() signal which - is emitted whenever a property created by this manager changes. - - \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtPointPropertyManager -*/ - -/*! - \fn void QtPointFPropertyManager::valueChanged(QtProperty *property, const QPointF &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtPointFPropertyManager::decimalsChanged(QtProperty *property, int prec) - - This signal is emitted whenever a property created by this manager - changes its precision of value, passing a pointer to the - \a property and the new \a prec value - - \sa setDecimals() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtPointFPropertyManager::QtPointFPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtPointFPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); - connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotDoubleChanged(QtProperty *, double))); - connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtPointFPropertyManager::~QtPointFPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e x and \e y - subproperties. - - In order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtDoublePropertyManager *QtPointFPropertyManager::subDoublePropertyManager() const -{ - return d_ptr->m_doublePropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns a point with coordinates (0, 0). - - \sa setValue() -*/ -QPointF QtPointFPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's step value. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa setSingleStep() -*/ -double QtPointFPropertyManager::singleStep(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtPointFPropertyManagerPrivate::Data::singleStep, property, 0); -} - -/*! - Returns the given \a property's precision, in decimals. - - \sa setDecimals() -*/ -int QtPointFPropertyManager::decimals(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtPointFPropertyManagerPrivate::Data::decimals, property, 0); -} - -/*! - \reimp -*/ -QString QtPointFPropertyManager::valueText(const QtProperty *property) const -{ - const QtPointFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QPointF v = it.value().val; - const int dec = it.value().decimals; - return QString(tr("(%1, %2)").arg(QString::number(v.x(), 'f', dec)) - .arg(QString::number(v.y(), 'f', dec))); -} - -/*! - \fn void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - \sa value(), valueChanged() -*/ -void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &val) -{ - const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - if (it.value().val == val) - return; - - it.value().val = val; - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); - - emit propertyChanged(property); - emit valueChanged(property, val); -} - -/*! - Sets the step value for the given \a property to \a step. - - The step is typically used to increment or decrement a property value while pressing an arrow key. - - \sa singleStep() -*/ -void QtPointFPropertyManager::setSingleStep(QtProperty *property, double step) -{ - const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtPointFPropertyManagerPrivate::Data data = it.value(); - - if (step < 0) - step = 0; - - if (data.singleStep == step) - return; - - data.singleStep = step; - d_ptr->m_doublePropertyManager->setSingleStep(d_ptr->m_propertyToX[property], step); - d_ptr->m_doublePropertyManager->setSingleStep(d_ptr->m_propertyToY[property], step); - - it.value() = data; - - emit singleStepChanged(property, data.singleStep); -} - -/*! - \fn void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) - - Sets the precision of the given \a property to \a prec. - - The valid decimal range is 0-13. The default is 2. - - \sa decimals() -*/ -void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) -{ - const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtPointFPropertyManagerPrivate::Data data = it.value(); - - if (prec > 13) - prec = 13; - else if (prec < 0) - prec = 0; - - if (data.decimals == prec) - return; - - data.decimals = prec; - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); - - it.value() = data; - - emit decimalsChanged(property, data.decimals); -} - -/*! - \reimp -*/ -void QtPointFPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtPointFPropertyManagerPrivate::Data(); - - QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); - xProp->setPropertyName(tr("X")); - d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(xProp, 0); - d_ptr->m_propertyToX[property] = xProp; - d_ptr->m_xToProperty[xProp] = property; - property->addSubProperty(xProp); - - QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); - yProp->setPropertyName(tr("Y")); - d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(yProp, 0); - d_ptr->m_propertyToY[property] = yProp; - d_ptr->m_yToProperty[yProp] = property; - property->addSubProperty(yProp); -} - -/*! - \reimp -*/ -void QtPointFPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *xProp = d_ptr->m_propertyToX[property]; - if (xProp) { - d_ptr->m_xToProperty.remove(xProp); - delete xProp; - } - d_ptr->m_propertyToX.remove(property); - - QtProperty *yProp = d_ptr->m_propertyToY[property]; - if (yProp) { - d_ptr->m_yToProperty.remove(yProp); - delete yProp; - } - d_ptr->m_propertyToY.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtSizePropertyManager - -class QtSizePropertyManagerPrivate -{ - QtSizePropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtSizePropertyManager) -public: - - void slotIntChanged(QtProperty *property, int value); - void slotPropertyDestroyed(QtProperty *property); - void setValue(QtProperty *property, const QSize &val); - void setRange(QtProperty *property, - const QSize &minVal, const QSize &maxVal, const QSize &val); - - struct Data - { - Data() : val(QSize(0, 0)), minVal(QSize(0, 0)), maxVal(QSize(INT_MAX, INT_MAX)) {} - QSize val; - QSize minVal; - QSize maxVal; - QSize minimumValue() const { return minVal; } - QSize maximumValue() const { return maxVal; } - void setMinimumValue(const QSize &newMinVal) { setSizeMinimumData(this, newMinVal); } - void setMaximumValue(const QSize &newMaxVal) { setSizeMaximumData(this, newMaxVal); } - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtIntPropertyManager *m_intPropertyManager; - - QMap m_propertyToW; - QMap m_propertyToH; - - QMap m_wToProperty; - QMap m_hToProperty; -}; - -void QtSizePropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) -{ - if (QtProperty *prop = m_wToProperty.value(property, 0)) { - QSize s = m_values[prop].val; - s.setWidth(value); - q_ptr->setValue(prop, s); - } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { - QSize s = m_values[prop].val; - s.setHeight(value); - q_ptr->setValue(prop, s); - } -} - -void QtSizePropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { - m_propertyToW[pointProp] = 0; - m_wToProperty.remove(property); - } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { - m_propertyToH[pointProp] = 0; - m_hToProperty.remove(property); - } -} - -void QtSizePropertyManagerPrivate::setValue(QtProperty *property, const QSize &val) -{ - m_intPropertyManager->setValue(m_propertyToW.value(property), val.width()); - m_intPropertyManager->setValue(m_propertyToH.value(property), val.height()); -} - -void QtSizePropertyManagerPrivate::setRange(QtProperty *property, - const QSize &minVal, const QSize &maxVal, const QSize &val) -{ - QtProperty *wProperty = m_propertyToW.value(property); - QtProperty *hProperty = m_propertyToH.value(property); - m_intPropertyManager->setRange(wProperty, minVal.width(), maxVal.width()); - m_intPropertyManager->setValue(wProperty, val.width()); - m_intPropertyManager->setRange(hProperty, minVal.height(), maxVal.height()); - m_intPropertyManager->setValue(hProperty, val.height()); -} - -/*! - \class QtSizePropertyManager - - \brief The QtSizePropertyManager provides and manages QSize properties. - - A size property has nested \e width and \e height - subproperties. The top-level property's value can be retrieved - using the value() function, and set using the setValue() slot. - - The subproperties are created by a QtIntPropertyManager object. This - manager can be retrieved using the subIntPropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - A size property also has a range of valid values defined by a - minimum size and a maximum size. These sizes can be retrieved - using the minimum() and the maximum() functions, and set using the - setMinimum() and setMaximum() slots. Alternatively, the range can - be defined in one go using the setRange() slot. - - In addition, QtSizePropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the rangeChanged() signal which is emitted whenever - such a property changes its range of valid sizes. - - \sa QtAbstractPropertyManager, QtIntPropertyManager, QtSizeFPropertyManager -*/ - -/*! - \fn void QtSizePropertyManager::valueChanged(QtProperty *property, const QSize &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtSizePropertyManager::rangeChanged(QtProperty *property, const QSize &minimum, const QSize &maximum) - - This signal is emitted whenever a property created by this manager - changes its range of valid sizes, passing a pointer to the \a - property and the new \a minimum and \a maximum sizes. - - \sa setRange() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtSizePropertyManager::QtSizePropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtSizePropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); - connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotIntChanged(QtProperty *, int))); - connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtSizePropertyManager::~QtSizePropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e width and \e height - subproperties. - - In order to provide editing widgets for the \e width and \e height - properties in a property browser widget, this manager must be - associated with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtIntPropertyManager *QtSizePropertyManager::subIntPropertyManager() const -{ - return d_ptr->m_intPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid size - - \sa setValue() -*/ -QSize QtSizePropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's minimum size value. - - \sa setMinimum(), maximum(), setRange() -*/ -QSize QtSizePropertyManager::minimum(const QtProperty *property) const -{ - return getMinimum(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's maximum size value. - - \sa setMaximum(), minimum(), setRange() -*/ -QSize QtSizePropertyManager::maximum(const QtProperty *property) const -{ - return getMaximum(d_ptr->m_values, property); -} - -/*! - \reimp -*/ -QString QtSizePropertyManager::valueText(const QtProperty *property) const -{ - const QtSizePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QSize v = it.value().val; - return QString(tr("%1 x %2").arg(QString::number(v.width())) - .arg(QString::number(v.height()))); -} - -/*! - \fn void QtSizePropertyManager::setValue(QtProperty *property, const QSize &value) - - Sets the value of the given \a property to \a value. - - If the specified \a value is not valid according to the given \a - property's size range, the \a value is adjusted to the nearest - valid value within the size range. - - \sa value(), setRange(), valueChanged() -*/ -void QtSizePropertyManager::setValue(QtProperty *property, const QSize &val) -{ - setValueInRange(this, d_ptr, - &QtSizePropertyManager::propertyChanged, - &QtSizePropertyManager::valueChanged, - property, val, &QtSizePropertyManagerPrivate::setValue); -} - -/*! - Sets the minimum size value for the given \a property to \a minVal. - - When setting the minimum size value, the maximum and current - values are adjusted if necessary (ensuring that the size range - remains valid and that the current value is within the range). - - \sa minimum(), setRange(), rangeChanged() -*/ -void QtSizePropertyManager::setMinimum(QtProperty *property, const QSize &minVal) -{ - setBorderValue(this, d_ptr, - &QtSizePropertyManager::propertyChanged, - &QtSizePropertyManager::valueChanged, - &QtSizePropertyManager::rangeChanged, - property, - &QtSizePropertyManagerPrivate::Data::minimumValue, - &QtSizePropertyManagerPrivate::Data::setMinimumValue, - minVal, &QtSizePropertyManagerPrivate::setRange); -} - -/*! - Sets the maximum size value for the given \a property to \a maxVal. - - When setting the maximum size value, the minimum and current - values are adjusted if necessary (ensuring that the size range - remains valid and that the current value is within the range). - - \sa maximum(), setRange(), rangeChanged() -*/ -void QtSizePropertyManager::setMaximum(QtProperty *property, const QSize &maxVal) -{ - setBorderValue(this, d_ptr, - &QtSizePropertyManager::propertyChanged, - &QtSizePropertyManager::valueChanged, - &QtSizePropertyManager::rangeChanged, - property, - &QtSizePropertyManagerPrivate::Data::maximumValue, - &QtSizePropertyManagerPrivate::Data::setMaximumValue, - maxVal, &QtSizePropertyManagerPrivate::setRange); -} - -/*! - \fn void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minimum, const QSize &maximum) - - Sets the range of valid values. - - This is a convenience function defining the range of valid values - in one go; setting the \a minimum and \a maximum values for the - given \a property with a single function call. - - When setting a new range, the current value is adjusted if - necessary (ensuring that the value remains within the range). - - \sa setMinimum(), setMaximum(), rangeChanged() -*/ -void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal) -{ - setBorderValues(this, d_ptr, - &QtSizePropertyManager::propertyChanged, - &QtSizePropertyManager::valueChanged, - &QtSizePropertyManager::rangeChanged, - property, minVal, maxVal, &QtSizePropertyManagerPrivate::setRange); -} - -/*! - \reimp -*/ -void QtSizePropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtSizePropertyManagerPrivate::Data(); - - QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); - wProp->setPropertyName(tr("Width")); - d_ptr->m_intPropertyManager->setValue(wProp, 0); - d_ptr->m_intPropertyManager->setMinimum(wProp, 0); - d_ptr->m_propertyToW[property] = wProp; - d_ptr->m_wToProperty[wProp] = property; - property->addSubProperty(wProp); - - QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); - hProp->setPropertyName(tr("Height")); - d_ptr->m_intPropertyManager->setValue(hProp, 0); - d_ptr->m_intPropertyManager->setMinimum(hProp, 0); - d_ptr->m_propertyToH[property] = hProp; - d_ptr->m_hToProperty[hProp] = property; - property->addSubProperty(hProp); -} - -/*! - \reimp -*/ -void QtSizePropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *wProp = d_ptr->m_propertyToW[property]; - if (wProp) { - d_ptr->m_wToProperty.remove(wProp); - delete wProp; - } - d_ptr->m_propertyToW.remove(property); - - QtProperty *hProp = d_ptr->m_propertyToH[property]; - if (hProp) { - d_ptr->m_hToProperty.remove(hProp); - delete hProp; - } - d_ptr->m_propertyToH.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtSizeFPropertyManager - -class QtSizeFPropertyManagerPrivate -{ - QtSizeFPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtSizeFPropertyManager) -public: - - void slotDoubleChanged(QtProperty *property, double value); - void slotPropertyDestroyed(QtProperty *property); - void setValue(QtProperty *property, const QSizeF &val); - void setRange(QtProperty *property, - const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val); - - struct Data - { - Data() : val(QSizeF(0, 0)), minVal(QSizeF(0, 0)), maxVal(QSizeF(INT_MAX, INT_MAX)), decimals(2) {} - QSizeF val; - QSizeF minVal; - QSizeF maxVal; - int decimals; - QSizeF minimumValue() const { return minVal; } - QSizeF maximumValue() const { return maxVal; } - void setMinimumValue(const QSizeF &newMinVal) { setSizeMinimumData(this, newMinVal); } - void setMaximumValue(const QSizeF &newMaxVal) { setSizeMaximumData(this, newMaxVal); } - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtDoublePropertyManager *m_doublePropertyManager; - - QMap m_propertyToW; - QMap m_propertyToH; - - QMap m_wToProperty; - QMap m_hToProperty; -}; - -void QtSizeFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) -{ - if (QtProperty *prop = m_wToProperty.value(property, 0)) { - QSizeF s = m_values[prop].val; - s.setWidth(value); - q_ptr->setValue(prop, s); - } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { - QSizeF s = m_values[prop].val; - s.setHeight(value); - q_ptr->setValue(prop, s); - } -} - -void QtSizeFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { - m_propertyToW[pointProp] = 0; - m_wToProperty.remove(property); - } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { - m_propertyToH[pointProp] = 0; - m_hToProperty.remove(property); - } -} - -void QtSizeFPropertyManagerPrivate::setValue(QtProperty *property, const QSizeF &val) -{ - m_doublePropertyManager->setValue(m_propertyToW.value(property), val.width()); - m_doublePropertyManager->setValue(m_propertyToH.value(property), val.height()); -} - -void QtSizeFPropertyManagerPrivate::setRange(QtProperty *property, - const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val) -{ - m_doublePropertyManager->setRange(m_propertyToW[property], minVal.width(), maxVal.width()); - m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); - m_doublePropertyManager->setRange(m_propertyToH[property], minVal.height(), maxVal.height()); - m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); -} - -/*! - \class QtSizeFPropertyManager - - \brief The QtSizeFPropertyManager provides and manages QSizeF properties. - - A size property has nested \e width and \e height - subproperties. The top-level property's value can be retrieved - using the value() function, and set using the setValue() slot. - - The subproperties are created by a QtDoublePropertyManager object. This - manager can be retrieved using the subDoublePropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - A size property also has a range of valid values defined by a - minimum size and a maximum size. These sizes can be retrieved - using the minimum() and the maximum() functions, and set using the - setMinimum() and setMaximum() slots. Alternatively, the range can - be defined in one go using the setRange() slot. - - In addition, QtSizeFPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the rangeChanged() signal which is emitted whenever - such a property changes its range of valid sizes. - - \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtSizePropertyManager -*/ - -/*! - \fn void QtSizeFPropertyManager::valueChanged(QtProperty *property, const QSizeF &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtSizeFPropertyManager::rangeChanged(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) - - This signal is emitted whenever a property created by this manager - changes its range of valid sizes, passing a pointer to the \a - property and the new \a minimum and \a maximum sizes. - - \sa setRange() -*/ - -/*! - \fn void QtSizeFPropertyManager::decimalsChanged(QtProperty *property, int prec) - - This signal is emitted whenever a property created by this manager - changes its precision of value, passing a pointer to the - \a property and the new \a prec value - - \sa setDecimals() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtSizeFPropertyManager::QtSizeFPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtSizeFPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); - connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotDoubleChanged(QtProperty *, double))); - connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtSizeFPropertyManager::~QtSizeFPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e width and \e height - subproperties. - - In order to provide editing widgets for the \e width and \e height - properties in a property browser widget, this manager must be - associated with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtDoublePropertyManager *QtSizeFPropertyManager::subDoublePropertyManager() const -{ - return d_ptr->m_doublePropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid size - - \sa setValue() -*/ -QSizeF QtSizeFPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's precision, in decimals. - - \sa setDecimals() -*/ -int QtSizeFPropertyManager::decimals(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtSizeFPropertyManagerPrivate::Data::decimals, property, 0); -} - -/*! - Returns the given \a property's minimum size value. - - \sa setMinimum(), maximum(), setRange() -*/ -QSizeF QtSizeFPropertyManager::minimum(const QtProperty *property) const -{ - return getMinimum(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's maximum size value. - - \sa setMaximum(), minimum(), setRange() -*/ -QSizeF QtSizeFPropertyManager::maximum(const QtProperty *property) const -{ - return getMaximum(d_ptr->m_values, property); -} - -/*! - \reimp -*/ -QString QtSizeFPropertyManager::valueText(const QtProperty *property) const -{ - const QtSizeFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QSizeF v = it.value().val; - const int dec = it.value().decimals; - return QString(tr("%1 x %2").arg(QString::number(v.width(), 'f', dec)) - .arg(QString::number(v.height(), 'f', dec))); -} - -/*! - \fn void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &value) - - Sets the value of the given \a property to \a value. - - If the specified \a value is not valid according to the given \a - property's size range, the \a value is adjusted to the nearest - valid value within the size range. - - \sa value(), setRange(), valueChanged() -*/ -void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &val) -{ - setValueInRange(this, d_ptr, - &QtSizeFPropertyManager::propertyChanged, - &QtSizeFPropertyManager::valueChanged, - property, val, &QtSizeFPropertyManagerPrivate::setValue); -} - -/*! - \fn void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) - - Sets the precision of the given \a property to \a prec. - - The valid decimal range is 0-13. The default is 2. - - \sa decimals() -*/ -void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) -{ - const QtSizeFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtSizeFPropertyManagerPrivate::Data data = it.value(); - - if (prec > 13) - prec = 13; - else if (prec < 0) - prec = 0; - - if (data.decimals == prec) - return; - - data.decimals = prec; - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); - - it.value() = data; - - emit decimalsChanged(property, data.decimals); -} - -/*! - Sets the minimum size value for the given \a property to \a minVal. - - When setting the minimum size value, the maximum and current - values are adjusted if necessary (ensuring that the size range - remains valid and that the current value is within the range). - - \sa minimum(), setRange(), rangeChanged() -*/ -void QtSizeFPropertyManager::setMinimum(QtProperty *property, const QSizeF &minVal) -{ - setBorderValue(this, d_ptr, - &QtSizeFPropertyManager::propertyChanged, - &QtSizeFPropertyManager::valueChanged, - &QtSizeFPropertyManager::rangeChanged, - property, - &QtSizeFPropertyManagerPrivate::Data::minimumValue, - &QtSizeFPropertyManagerPrivate::Data::setMinimumValue, - minVal, &QtSizeFPropertyManagerPrivate::setRange); -} - -/*! - Sets the maximum size value for the given \a property to \a maxVal. - - When setting the maximum size value, the minimum and current - values are adjusted if necessary (ensuring that the size range - remains valid and that the current value is within the range). - - \sa maximum(), setRange(), rangeChanged() -*/ -void QtSizeFPropertyManager::setMaximum(QtProperty *property, const QSizeF &maxVal) -{ - setBorderValue(this, d_ptr, - &QtSizeFPropertyManager::propertyChanged, - &QtSizeFPropertyManager::valueChanged, - &QtSizeFPropertyManager::rangeChanged, - property, - &QtSizeFPropertyManagerPrivate::Data::maximumValue, - &QtSizeFPropertyManagerPrivate::Data::setMaximumValue, - maxVal, &QtSizeFPropertyManagerPrivate::setRange); -} - -/*! - \fn void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) - - Sets the range of valid values. - - This is a convenience function defining the range of valid values - in one go; setting the \a minimum and \a maximum values for the - given \a property with a single function call. - - When setting a new range, the current value is adjusted if - necessary (ensuring that the value remains within the range). - - \sa setMinimum(), setMaximum(), rangeChanged() -*/ -void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal) -{ - setBorderValues(this, d_ptr, - &QtSizeFPropertyManager::propertyChanged, - &QtSizeFPropertyManager::valueChanged, - &QtSizeFPropertyManager::rangeChanged, - property, minVal, maxVal, &QtSizeFPropertyManagerPrivate::setRange); -} - -/*! - \reimp -*/ -void QtSizeFPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtSizeFPropertyManagerPrivate::Data(); - - QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); - wProp->setPropertyName(tr("Width")); - d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(wProp, 0); - d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); - d_ptr->m_propertyToW[property] = wProp; - d_ptr->m_wToProperty[wProp] = property; - property->addSubProperty(wProp); - - QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); - hProp->setPropertyName(tr("Height")); - d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(hProp, 0); - d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); - d_ptr->m_propertyToH[property] = hProp; - d_ptr->m_hToProperty[hProp] = property; - property->addSubProperty(hProp); -} - -/*! - \reimp -*/ -void QtSizeFPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *wProp = d_ptr->m_propertyToW[property]; - if (wProp) { - d_ptr->m_wToProperty.remove(wProp); - delete wProp; - } - d_ptr->m_propertyToW.remove(property); - - QtProperty *hProp = d_ptr->m_propertyToH[property]; - if (hProp) { - d_ptr->m_hToProperty.remove(hProp); - delete hProp; - } - d_ptr->m_propertyToH.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtRectPropertyManager - -class QtRectPropertyManagerPrivate -{ - QtRectPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtRectPropertyManager) -public: - - void slotIntChanged(QtProperty *property, int value); - void slotPropertyDestroyed(QtProperty *property); - void setConstraint(QtProperty *property, const QRect &constraint, const QRect &val); - - struct Data - { - Data() : val(0, 0, 0, 0) {} - QRect val; - QRect constraint; - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtIntPropertyManager *m_intPropertyManager; - - QMap m_propertyToX; - QMap m_propertyToY; - QMap m_propertyToW; - QMap m_propertyToH; - - QMap m_xToProperty; - QMap m_yToProperty; - QMap m_wToProperty; - QMap m_hToProperty; -}; - -void QtRectPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) -{ - if (QtProperty *prop = m_xToProperty.value(property, 0)) { - QRect r = m_values[prop].val; - r.moveLeft(value); - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_yToProperty.value(property)) { - QRect r = m_values[prop].val; - r.moveTop(value); - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { - Data data = m_values[prop]; - QRect r = data.val; - r.setWidth(value); - if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { - r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); - } - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { - Data data = m_values[prop]; - QRect r = data.val; - r.setHeight(value); - if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { - r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); - } - q_ptr->setValue(prop, r); - } -} - -void QtRectPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { - m_propertyToX[pointProp] = 0; - m_xToProperty.remove(property); - } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { - m_propertyToY[pointProp] = 0; - m_yToProperty.remove(property); - } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { - m_propertyToW[pointProp] = 0; - m_wToProperty.remove(property); - } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { - m_propertyToH[pointProp] = 0; - m_hToProperty.remove(property); - } -} - -void QtRectPropertyManagerPrivate::setConstraint(QtProperty *property, - const QRect &constraint, const QRect &val) -{ - const bool isNull = constraint.isNull(); - const int left = isNull ? INT_MIN : constraint.left(); - const int right = isNull ? INT_MAX : constraint.left() + constraint.width(); - const int top = isNull ? INT_MIN : constraint.top(); - const int bottom = isNull ? INT_MAX : constraint.top() + constraint.height(); - const int width = isNull ? INT_MAX : constraint.width(); - const int height = isNull ? INT_MAX : constraint.height(); - - m_intPropertyManager->setRange(m_propertyToX[property], left, right); - m_intPropertyManager->setRange(m_propertyToY[property], top, bottom); - m_intPropertyManager->setRange(m_propertyToW[property], 0, width); - m_intPropertyManager->setRange(m_propertyToH[property], 0, height); - - m_intPropertyManager->setValue(m_propertyToX[property], val.x()); - m_intPropertyManager->setValue(m_propertyToY[property], val.y()); - m_intPropertyManager->setValue(m_propertyToW[property], val.width()); - m_intPropertyManager->setValue(m_propertyToH[property], val.height()); -} - -/*! - \class QtRectPropertyManager - - \brief The QtRectPropertyManager provides and manages QRect properties. - - A rectangle property has nested \e x, \e y, \e width and \e height - subproperties. The top-level property's value can be retrieved - using the value() function, and set using the setValue() slot. - - The subproperties are created by a QtIntPropertyManager object. This - manager can be retrieved using the subIntPropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - A rectangle property also has a constraint rectangle which can be - retrieved using the constraint() function, and set using the - setConstraint() slot. - - In addition, QtRectPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the constraintChanged() signal which is emitted - whenever such a property changes its constraint rectangle. - - \sa QtAbstractPropertyManager, QtIntPropertyManager, QtRectFPropertyManager -*/ - -/*! - \fn void QtRectPropertyManager::valueChanged(QtProperty *property, const QRect &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtRectPropertyManager::constraintChanged(QtProperty *property, const QRect &constraint) - - This signal is emitted whenever property changes its constraint - rectangle, passing a pointer to the \a property and the new \a - constraint rectangle as parameters. - - \sa setConstraint() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtRectPropertyManager::QtRectPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtRectPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); - connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotIntChanged(QtProperty *, int))); - connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtRectPropertyManager::~QtRectPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e x, \e y, \e width - and \e height subproperties. - - In order to provide editing widgets for the mentioned - subproperties in a property browser widget, this manager must be - associated with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtIntPropertyManager *QtRectPropertyManager::subIntPropertyManager() const -{ - return d_ptr->m_intPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid rectangle. - - \sa setValue(), constraint() -*/ -QRect QtRectPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's constraining rectangle. If returned value is null QRect it means there is no constraint applied. - - \sa value(), setConstraint() -*/ -QRect QtRectPropertyManager::constraint(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtRectPropertyManagerPrivate::Data::constraint, property, QRect()); -} - -/*! - \reimp -*/ -QString QtRectPropertyManager::valueText(const QtProperty *property) const -{ - const QtRectPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QRect v = it.value().val; - return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x())) - .arg(QString::number(v.y())) - .arg(QString::number(v.width())) - .arg(QString::number(v.height()))); -} - -/*! - \fn void QtRectPropertyManager::setValue(QtProperty *property, const QRect &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - If the specified \a value is not inside the given \a property's - constraining rectangle, the value is adjusted accordingly to fit - within the constraint. - - \sa value(), setConstraint(), valueChanged() -*/ -void QtRectPropertyManager::setValue(QtProperty *property, const QRect &val) -{ - const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtRectPropertyManagerPrivate::Data data = it.value(); - - QRect newRect = val.normalized(); - if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { - const QRect r1 = data.constraint; - const QRect r2 = newRect; - newRect.setLeft(qMax(r1.left(), r2.left())); - newRect.setRight(qMin(r1.right(), r2.right())); - newRect.setTop(qMax(r1.top(), r2.top())); - newRect.setBottom(qMin(r1.bottom(), r2.bottom())); - if (newRect.width() < 0 || newRect.height() < 0) - return; - } - - if (data.val == newRect) - return; - - data.val = newRect; - - it.value() = data; - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the given \a property's constraining rectangle to \a - constraint. - - When setting the constraint, the current value is adjusted if - necessary (ensuring that the current rectangle value is inside the - constraint). In order to reset the constraint pass a null QRect value. - - \sa setValue(), constraint(), constraintChanged() -*/ -void QtRectPropertyManager::setConstraint(QtProperty *property, const QRect &constraint) -{ - const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtRectPropertyManagerPrivate::Data data = it.value(); - - QRect newConstraint = constraint.normalized(); - if (data.constraint == newConstraint) - return; - - const QRect oldVal = data.val; - - data.constraint = newConstraint; - - if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { - QRect r1 = data.constraint; - QRect r2 = data.val; - - if (r2.width() > r1.width()) - r2.setWidth(r1.width()); - if (r2.height() > r1.height()) - r2.setHeight(r1.height()); - if (r2.left() < r1.left()) - r2.moveLeft(r1.left()); - else if (r2.right() > r1.right()) - r2.moveRight(r1.right()); - if (r2.top() < r1.top()) - r2.moveTop(r1.top()); - else if (r2.bottom() > r1.bottom()) - r2.moveBottom(r1.bottom()); - - data.val = r2; - } - - it.value() = data; - - emit constraintChanged(property, data.constraint); - - d_ptr->setConstraint(property, data.constraint, data.val); - - if (data.val == oldVal) - return; - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - \reimp -*/ -void QtRectPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtRectPropertyManagerPrivate::Data(); - - QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); - xProp->setPropertyName(tr("X")); - d_ptr->m_intPropertyManager->setValue(xProp, 0); - d_ptr->m_propertyToX[property] = xProp; - d_ptr->m_xToProperty[xProp] = property; - property->addSubProperty(xProp); - - QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); - yProp->setPropertyName(tr("Y")); - d_ptr->m_intPropertyManager->setValue(yProp, 0); - d_ptr->m_propertyToY[property] = yProp; - d_ptr->m_yToProperty[yProp] = property; - property->addSubProperty(yProp); - - QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); - wProp->setPropertyName(tr("Width")); - d_ptr->m_intPropertyManager->setValue(wProp, 0); - d_ptr->m_intPropertyManager->setMinimum(wProp, 0); - d_ptr->m_propertyToW[property] = wProp; - d_ptr->m_wToProperty[wProp] = property; - property->addSubProperty(wProp); - - QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); - hProp->setPropertyName(tr("Height")); - d_ptr->m_intPropertyManager->setValue(hProp, 0); - d_ptr->m_intPropertyManager->setMinimum(hProp, 0); - d_ptr->m_propertyToH[property] = hProp; - d_ptr->m_hToProperty[hProp] = property; - property->addSubProperty(hProp); -} - -/*! - \reimp -*/ -void QtRectPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *xProp = d_ptr->m_propertyToX[property]; - if (xProp) { - d_ptr->m_xToProperty.remove(xProp); - delete xProp; - } - d_ptr->m_propertyToX.remove(property); - - QtProperty *yProp = d_ptr->m_propertyToY[property]; - if (yProp) { - d_ptr->m_yToProperty.remove(yProp); - delete yProp; - } - d_ptr->m_propertyToY.remove(property); - - QtProperty *wProp = d_ptr->m_propertyToW[property]; - if (wProp) { - d_ptr->m_wToProperty.remove(wProp); - delete wProp; - } - d_ptr->m_propertyToW.remove(property); - - QtProperty *hProp = d_ptr->m_propertyToH[property]; - if (hProp) { - d_ptr->m_hToProperty.remove(hProp); - delete hProp; - } - d_ptr->m_propertyToH.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtRectFPropertyManager - -class QtRectFPropertyManagerPrivate -{ - QtRectFPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtRectFPropertyManager) -public: - - void slotDoubleChanged(QtProperty *property, double value); - void slotPropertyDestroyed(QtProperty *property); - void setConstraint(QtProperty *property, const QRectF &constraint, const QRectF &val); - - struct Data - { - Data() : val(0, 0, 0, 0), decimals(2) {} - QRectF val; - QRectF constraint; - int decimals; - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtDoublePropertyManager *m_doublePropertyManager; - - QMap m_propertyToX; - QMap m_propertyToY; - QMap m_propertyToW; - QMap m_propertyToH; - - QMap m_xToProperty; - QMap m_yToProperty; - QMap m_wToProperty; - QMap m_hToProperty; -}; - -void QtRectFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) -{ - if (QtProperty *prop = m_xToProperty.value(property, 0)) { - QRectF r = m_values[prop].val; - r.moveLeft(value); - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { - QRectF r = m_values[prop].val; - r.moveTop(value); - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { - Data data = m_values[prop]; - QRectF r = data.val; - r.setWidth(value); - if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { - r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); - } - q_ptr->setValue(prop, r); - } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { - Data data = m_values[prop]; - QRectF r = data.val; - r.setHeight(value); - if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { - r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); - } - q_ptr->setValue(prop, r); - } -} - -void QtRectFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { - m_propertyToX[pointProp] = 0; - m_xToProperty.remove(property); - } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { - m_propertyToY[pointProp] = 0; - m_yToProperty.remove(property); - } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { - m_propertyToW[pointProp] = 0; - m_wToProperty.remove(property); - } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { - m_propertyToH[pointProp] = 0; - m_hToProperty.remove(property); - } -} - -void QtRectFPropertyManagerPrivate::setConstraint(QtProperty *property, - const QRectF &constraint, const QRectF &val) -{ - const bool isNull = constraint.isNull(); - const float left = isNull ? FLT_MIN : constraint.left(); - const float right = isNull ? FLT_MAX : constraint.left() + constraint.width(); - const float top = isNull ? FLT_MIN : constraint.top(); - const float bottom = isNull ? FLT_MAX : constraint.top() + constraint.height(); - const float width = isNull ? FLT_MAX : constraint.width(); - const float height = isNull ? FLT_MAX : constraint.height(); - - m_doublePropertyManager->setRange(m_propertyToX[property], left, right); - m_doublePropertyManager->setRange(m_propertyToY[property], top, bottom); - m_doublePropertyManager->setRange(m_propertyToW[property], 0, width); - m_doublePropertyManager->setRange(m_propertyToH[property], 0, height); - - m_doublePropertyManager->setValue(m_propertyToX[property], val.x()); - m_doublePropertyManager->setValue(m_propertyToY[property], val.y()); - m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); - m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); -} - -/*! - \class QtRectFPropertyManager - - \brief The QtRectFPropertyManager provides and manages QRectF properties. - - A rectangle property has nested \e x, \e y, \e width and \e height - subproperties. The top-level property's value can be retrieved - using the value() function, and set using the setValue() slot. - - The subproperties are created by a QtDoublePropertyManager object. This - manager can be retrieved using the subDoublePropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - A rectangle property also has a constraint rectangle which can be - retrieved using the constraint() function, and set using the - setConstraint() slot. - - In addition, QtRectFPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the constraintChanged() signal which is emitted - whenever such a property changes its constraint rectangle. - - \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtRectPropertyManager -*/ - -/*! - \fn void QtRectFPropertyManager::valueChanged(QtProperty *property, const QRectF &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtRectFPropertyManager::constraintChanged(QtProperty *property, const QRectF &constraint) - - This signal is emitted whenever property changes its constraint - rectangle, passing a pointer to the \a property and the new \a - constraint rectangle as parameters. - - \sa setConstraint() -*/ - -/*! - \fn void QtRectFPropertyManager::decimalsChanged(QtProperty *property, int prec) - - This signal is emitted whenever a property created by this manager - changes its precision of value, passing a pointer to the - \a property and the new \a prec value - - \sa setDecimals() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtRectFPropertyManager::QtRectFPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtRectFPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); - connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotDoubleChanged(QtProperty *, double))); - connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtRectFPropertyManager::~QtRectFPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the nested \e x, \e y, \e width - and \e height subproperties. - - In order to provide editing widgets for the mentioned - subproperties in a property browser widget, this manager must be - associated with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtDoublePropertyManager *QtRectFPropertyManager::subDoublePropertyManager() const -{ - return d_ptr->m_doublePropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid rectangle. - - \sa setValue(), constraint() -*/ -QRectF QtRectFPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property); -} - -/*! - Returns the given \a property's precision, in decimals. - - \sa setDecimals() -*/ -int QtRectFPropertyManager::decimals(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::decimals, property, 0); -} - -/*! - Returns the given \a property's constraining rectangle. If returned value is null QRectF it means there is no constraint applied. - - \sa value(), setConstraint() -*/ -QRectF QtRectFPropertyManager::constraint(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::constraint, property, QRect()); -} - -/*! - \reimp -*/ -QString QtRectFPropertyManager::valueText(const QtProperty *property) const -{ - const QtRectFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - const QRectF v = it.value().val; - const int dec = it.value().decimals; - return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x(), 'f', dec)) - .arg(QString::number(v.y(), 'f', dec)) - .arg(QString::number(v.width(), 'f', dec)) - .arg(QString::number(v.height(), 'f', dec))); -} - -/*! - \fn void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - If the specified \a value is not inside the given \a property's - constraining rectangle, the value is adjusted accordingly to fit - within the constraint. - - \sa value(), setConstraint(), valueChanged() -*/ -void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &val) -{ - const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtRectFPropertyManagerPrivate::Data data = it.value(); - - QRectF newRect = val.normalized(); - if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { - const QRectF r1 = data.constraint; - const QRectF r2 = newRect; - newRect.setLeft(qMax(r1.left(), r2.left())); - newRect.setRight(qMin(r1.right(), r2.right())); - newRect.setTop(qMax(r1.top(), r2.top())); - newRect.setBottom(qMin(r1.bottom(), r2.bottom())); - if (newRect.width() < 0 || newRect.height() < 0) - return; - } - - if (data.val == newRect) - return; - - data.val = newRect; - - it.value() = data; - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); - d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the given \a property's constraining rectangle to \a - constraint. - - When setting the constraint, the current value is adjusted if - necessary (ensuring that the current rectangle value is inside the - constraint). In order to reset the constraint pass a null QRectF value. - - \sa setValue(), constraint(), constraintChanged() -*/ -void QtRectFPropertyManager::setConstraint(QtProperty *property, const QRectF &constraint) -{ - const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtRectFPropertyManagerPrivate::Data data = it.value(); - - QRectF newConstraint = constraint.normalized(); - if (data.constraint == newConstraint) - return; - - const QRectF oldVal = data.val; - - data.constraint = newConstraint; - - if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { - QRectF r1 = data.constraint; - QRectF r2 = data.val; - - if (r2.width() > r1.width()) - r2.setWidth(r1.width()); - if (r2.height() > r1.height()) - r2.setHeight(r1.height()); - if (r2.left() < r1.left()) - r2.moveLeft(r1.left()); - else if (r2.right() > r1.right()) - r2.moveRight(r1.right()); - if (r2.top() < r1.top()) - r2.moveTop(r1.top()); - else if (r2.bottom() > r1.bottom()) - r2.moveBottom(r1.bottom()); - - data.val = r2; - } - - it.value() = data; - - emit constraintChanged(property, data.constraint); - - d_ptr->setConstraint(property, data.constraint, data.val); - - if (data.val == oldVal) - return; - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - \fn void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) - - Sets the precision of the given \a property to \a prec. - - The valid decimal range is 0-13. The default is 2. - - \sa decimals() -*/ -void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) -{ - const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtRectFPropertyManagerPrivate::Data data = it.value(); - - if (prec > 13) - prec = 13; - else if (prec < 0) - prec = 0; - - if (data.decimals == prec) - return; - - data.decimals = prec; - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); - d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); - - it.value() = data; - - emit decimalsChanged(property, data.decimals); -} - -/*! - \reimp -*/ -void QtRectFPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtRectFPropertyManagerPrivate::Data(); - - QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); - xProp->setPropertyName(tr("X")); - d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(xProp, 0); - d_ptr->m_propertyToX[property] = xProp; - d_ptr->m_xToProperty[xProp] = property; - property->addSubProperty(xProp); - - QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); - yProp->setPropertyName(tr("Y")); - d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(yProp, 0); - d_ptr->m_propertyToY[property] = yProp; - d_ptr->m_yToProperty[yProp] = property; - property->addSubProperty(yProp); - - QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); - wProp->setPropertyName(tr("Width")); - d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(wProp, 0); - d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); - d_ptr->m_propertyToW[property] = wProp; - d_ptr->m_wToProperty[wProp] = property; - property->addSubProperty(wProp); - - QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); - hProp->setPropertyName(tr("Height")); - d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); - d_ptr->m_doublePropertyManager->setValue(hProp, 0); - d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); - d_ptr->m_propertyToH[property] = hProp; - d_ptr->m_hToProperty[hProp] = property; - property->addSubProperty(hProp); -} - -/*! - \reimp -*/ -void QtRectFPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *xProp = d_ptr->m_propertyToX[property]; - if (xProp) { - d_ptr->m_xToProperty.remove(xProp); - delete xProp; - } - d_ptr->m_propertyToX.remove(property); - - QtProperty *yProp = d_ptr->m_propertyToY[property]; - if (yProp) { - d_ptr->m_yToProperty.remove(yProp); - delete yProp; - } - d_ptr->m_propertyToY.remove(property); - - QtProperty *wProp = d_ptr->m_propertyToW[property]; - if (wProp) { - d_ptr->m_wToProperty.remove(wProp); - delete wProp; - } - d_ptr->m_propertyToW.remove(property); - - QtProperty *hProp = d_ptr->m_propertyToH[property]; - if (hProp) { - d_ptr->m_hToProperty.remove(hProp); - delete hProp; - } - d_ptr->m_propertyToH.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtEnumPropertyManager - -class QtEnumPropertyManagerPrivate -{ - QtEnumPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtEnumPropertyManager) -public: - - struct Data - { - Data() : val(-1) {} - int val; - QStringList enumNames; - QMap enumIcons; - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! - \class QtEnumPropertyManager - - \brief The QtEnumPropertyManager provides and manages enum properties. - - Each enum property has an associated list of enum names which can - be retrieved using the enumNames() function, and set using the - corresponding setEnumNames() function. An enum property's value is - represented by an index in this list, and can be retrieved and set - using the value() and setValue() slots respectively. - - Each enum value can also have an associated icon. The mapping from - values to icons can be set using the setEnumIcons() function and - queried with the enumIcons() function. - - In addition, QtEnumPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. The enumNamesChanged() or enumIconsChanged() signal is emitted - whenever the list of enum names or icons is altered. - - \sa QtAbstractPropertyManager, QtEnumEditorFactory -*/ - -/*! - \fn void QtEnumPropertyManager::valueChanged(QtProperty *property, int value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtEnumPropertyManager::enumNamesChanged(QtProperty *property, const QStringList &names) - - This signal is emitted whenever a property created by this manager - changes its enum names, passing a pointer to the \a property and - the new \a names as parameters. - - \sa setEnumNames() -*/ - -/*! - \fn void QtEnumPropertyManager::enumIconsChanged(QtProperty *property, const QMap &icons) - - This signal is emitted whenever a property created by this manager - changes its enum icons, passing a pointer to the \a property and - the new mapping of values to \a icons as parameters. - - \sa setEnumIcons() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtEnumPropertyManager::QtEnumPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtEnumPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtEnumPropertyManager::~QtEnumPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value which is an index in the - list returned by enumNames() - - If the given property is not managed by this manager, this - function returns -1. - - \sa enumNames(), setValue() -*/ -int QtEnumPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property, -1); -} - -/*! - Returns the given \a property's list of enum names. - - \sa value(), setEnumNames() -*/ -QStringList QtEnumPropertyManager::enumNames(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumNames, property, QStringList()); -} - -/*! - Returns the given \a property's map of enum values to their icons. - - \sa value(), setEnumIcons() -*/ -QMap QtEnumPropertyManager::enumIcons(const QtProperty *property) const -{ - return getData >(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumIcons, property, QMap()); -} - -/*! - \reimp -*/ -QString QtEnumPropertyManager::valueText(const QtProperty *property) const -{ - const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - const QtEnumPropertyManagerPrivate::Data &data = it.value(); - - const int v = data.val; - if (v >= 0 && v < data.enumNames.count()) - return data.enumNames.at(v); - return QString(); -} - -/*! - \reimp -*/ -QIcon QtEnumPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QIcon(); - - const QtEnumPropertyManagerPrivate::Data &data = it.value(); - - const int v = data.val; - return data.enumIcons.value(v); -} - -/*! - \fn void QtEnumPropertyManager::setValue(QtProperty *property, int value) - - Sets the value of the given \a property to \a value. - - The specified \a value must be less than the size of the given \a - property's enumNames() list, and larger than (or equal to) 0. - - \sa value(), valueChanged() -*/ -void QtEnumPropertyManager::setValue(QtProperty *property, int val) -{ - const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtEnumPropertyManagerPrivate::Data data = it.value(); - - if (val >= data.enumNames.count()) - return; - - if (val < 0 && data.enumNames.count() > 0) - return; - - if (val < 0) - val = -1; - - if (data.val == val) - return; - - data.val = val; - - it.value() = data; - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the given \a property's list of enum names to \a - enumNames. The \a property's current value is bound to a valid index. - - If the specified \a enumNames list is empty, the \a property's - current value is set to -1. - - \sa enumNames(), enumNamesChanged() -*/ -void QtEnumPropertyManager::setEnumNames(QtProperty *property, const QStringList &enumNames) -{ - const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtEnumPropertyManagerPrivate::Data data = it.value(); - - if (data.enumNames == enumNames) - return; - - data.enumNames = enumNames; - data.val = qBound(-1, data.val, enumNames.count() - 1); - - it.value() = data; - - emit enumNamesChanged(property, data.enumNames); - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the given \a property's map of enum values to their icons to \a - enumIcons. - - Each enum value can have associated icon. This association is represented with passed \a enumIcons map. - - \sa enumNames(), enumNamesChanged() -*/ -void QtEnumPropertyManager::setEnumIcons(QtProperty *property, const QMap &enumIcons) -{ - const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - it.value().enumIcons = enumIcons; - - emit enumIconsChanged(property, it.value().enumIcons); - - emit propertyChanged(property); -} - -/*! - \reimp -*/ -void QtEnumPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtEnumPropertyManagerPrivate::Data(); -} - -/*! - \reimp -*/ -void QtEnumPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -// QtFlagPropertyManager - -class QtFlagPropertyManagerPrivate -{ - QtFlagPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtFlagPropertyManager) -public: - - void slotBoolChanged(QtProperty *property, bool value); - void slotPropertyDestroyed(QtProperty *property); - - struct Data - { - Data() : val(-1) {} - int val; - QStringList flagNames; - }; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtBoolPropertyManager *m_boolPropertyManager; - - QMap > m_propertyToFlags; - - QMap m_flagToProperty; -}; - -void QtFlagPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) -{ - QtProperty *prop = m_flagToProperty.value(property, 0); - if (prop == 0) - return; - - QListIterator itProp(m_propertyToFlags[prop]); - int level = 0; - while (itProp.hasNext()) { - QtProperty *p = itProp.next(); - if (p == property) { - int v = m_values[prop].val; - if (value) { - v |= (1 << level); - } else { - v &= ~(1 << level); - } - q_ptr->setValue(prop, v); - return; - } - level++; - } -} - -void QtFlagPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - QtProperty *flagProperty = m_flagToProperty.value(property, 0); - if (flagProperty == 0) - return; - - m_propertyToFlags[flagProperty].replace(m_propertyToFlags[flagProperty].indexOf(property), 0); - m_flagToProperty.remove(property); -} - -/*! - \class QtFlagPropertyManager - - \brief The QtFlagPropertyManager provides and manages flag properties. - - Each flag property has an associated list of flag names which can - be retrieved using the flagNames() function, and set using the - corresponding setFlagNames() function. - - The flag manager provides properties with nested boolean - subproperties representing each flag, i.e. a flag property's value - is the binary combination of the subproperties' values. A - property's value can be retrieved and set using the value() and - setValue() slots respectively. The combination of flags is represented - by single int value - that's why it's possible to store up to - 32 independent flags in one flag property. - - The subproperties are created by a QtBoolPropertyManager object. This - manager can be retrieved using the subBoolPropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - In addition, QtFlagPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes, and the flagNamesChanged() signal which is emitted - whenever the list of flag names is altered. - - \sa QtAbstractPropertyManager, QtBoolPropertyManager -*/ - -/*! - \fn void QtFlagPropertyManager::valueChanged(QtProperty *property, int value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtFlagPropertyManager::flagNamesChanged(QtProperty *property, const QStringList &names) - - This signal is emitted whenever a property created by this manager - changes its flag names, passing a pointer to the \a property and the - new \a names as parameters. - - \sa setFlagNames() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtFlagPropertyManager::QtFlagPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtFlagPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); - connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotBoolChanged(QtProperty *, bool))); - connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtFlagPropertyManager::~QtFlagPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that produces the nested boolean subproperties - representing each flag. - - In order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtBoolPropertyManager *QtFlagPropertyManager::subBoolPropertyManager() const -{ - return d_ptr->m_boolPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns 0. - - \sa flagNames(), setValue() -*/ -int QtFlagPropertyManager::value(const QtProperty *property) const -{ - return getValue(d_ptr->m_values, property, 0); -} - -/*! - Returns the given \a property's list of flag names. - - \sa value(), setFlagNames() -*/ -QStringList QtFlagPropertyManager::flagNames(const QtProperty *property) const -{ - return getData(d_ptr->m_values, &QtFlagPropertyManagerPrivate::Data::flagNames, property, QStringList()); -} - -/*! - \reimp -*/ -QString QtFlagPropertyManager::valueText(const QtProperty *property) const -{ - const QtFlagPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - const QtFlagPropertyManagerPrivate::Data &data = it.value(); - - QString str; - int level = 0; - const QChar bar = QLatin1Char('|'); - const QStringList::const_iterator fncend = data.flagNames.constEnd(); - for (QStringList::const_iterator it = data.flagNames.constBegin(); it != fncend; ++it) { - if (data.val & (1 << level)) { - if (!str.isEmpty()) - str += bar; - str += *it; - } - - level++; - } - return str; -} - -/*! - \fn void QtFlagPropertyManager::setValue(QtProperty *property, int value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - The specified \a value must be less than the binary combination of - the property's flagNames() list size (i.e. less than 2\sup n, - where \c n is the size of the list) and larger than (or equal to) - 0. - - \sa value(), valueChanged() -*/ -void QtFlagPropertyManager::setValue(QtProperty *property, int val) -{ - const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtFlagPropertyManagerPrivate::Data data = it.value(); - - if (data.val == val) - return; - - if (val < 0) - return; - - if (static_cast(val) >= (1u << data.flagNames.count())) - return; - - data.val = val; - - it.value() = data; - - QListIterator itProp(d_ptr->m_propertyToFlags[property]); - int level = 0; - while (itProp.hasNext()) { - QtProperty *prop = itProp.next(); - if (prop) - d_ptr->m_boolPropertyManager->setValue(prop, val & (1 << level)); - level++; - } - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - Sets the given \a property's list of flag names to \a flagNames. The - property's current value is cleared of any flags for which no name is - provided. - - \sa flagNames(), flagNamesChanged() -*/ -void QtFlagPropertyManager::setFlagNames(QtProperty *property, const QStringList &flagNames) -{ - const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - QtFlagPropertyManagerPrivate::Data data = it.value(); - - if (data.flagNames == flagNames) - return; - - data.flagNames = flagNames; - data.val &= ((1 << data.flagNames.count()) - 1); - - it.value() = data; - - QListIterator itProp(d_ptr->m_propertyToFlags[property]); - while (itProp.hasNext()) { - QtProperty *prop = itProp.next(); - if (prop) { - d_ptr->m_flagToProperty.remove(prop); - delete prop; - } - } - d_ptr->m_propertyToFlags[property].clear(); - - QStringListIterator itFlag(flagNames); - int level = 0; - while (itFlag.hasNext()) { - const QString flagName = itFlag.next(); - QtProperty *prop = d_ptr->m_boolPropertyManager->addProperty(flagName); - d_ptr->m_boolPropertyManager->setValue(prop, data.val & (1 << level)); - property->addSubProperty(prop); - d_ptr->m_propertyToFlags[property].append(prop); - d_ptr->m_flagToProperty[prop] = property; - level++; - } - - emit flagNamesChanged(property, data.flagNames); - - emit propertyChanged(property); - emit valueChanged(property, data.val); -} - -/*! - \reimp -*/ -void QtFlagPropertyManager::initializeProperty(QtProperty *property) -{ - d_ptr->m_values[property] = QtFlagPropertyManagerPrivate::Data(); - - d_ptr->m_propertyToFlags[property] = QList(); -} - -/*! - \reimp -*/ -void QtFlagPropertyManager::uninitializeProperty(QtProperty *property) -{ - QListIterator itProp(d_ptr->m_propertyToFlags[property]); - while (itProp.hasNext()) { - QtProperty *prop = itProp.next(); - if (prop) { - d_ptr->m_flagToProperty.remove(prop); - delete prop; - } - } - d_ptr->m_propertyToFlags.remove(property); - - d_ptr->m_values.remove(property); -} - - -// QtFontPropertyManager: -// QtFontPropertyManagerPrivate has a mechanism for reacting -// to QApplication::fontDatabaseChanged() [4.5], which is emitted -// when someone loads an application font. The signals are compressed -// using a timer with interval 0, which then causes the family -// enumeration manager to re-set its strings and index values -// for each property. - -Q_GLOBAL_STATIC(QFontDatabase, fontDatabase) - -class QtFontPropertyManagerPrivate -{ - QtFontPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtFontPropertyManager) -public: - - QtFontPropertyManagerPrivate(); - - void slotIntChanged(QtProperty *property, int value); - void slotEnumChanged(QtProperty *property, int value); - void slotBoolChanged(QtProperty *property, bool value); - void slotPropertyDestroyed(QtProperty *property); - void slotFontDatabaseChanged(); - void slotFontDatabaseDelayedChange(); - - QStringList m_familyNames; - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtIntPropertyManager *m_intPropertyManager; - QtEnumPropertyManager *m_enumPropertyManager; - QtBoolPropertyManager *m_boolPropertyManager; - - QMap m_propertyToFamily; - QMap m_propertyToPixelSize; - QMap m_propertyToBold; - QMap m_propertyToItalic; - QMap m_propertyToUnderline; - QMap m_propertyToStrikeOut; - QMap m_propertyToKerning; - - QMap m_familyToProperty; - QMap m_pixelSizeToProperty; - QMap m_boldToProperty; - QMap m_italicToProperty; - QMap m_underlineToProperty; - QMap m_strikeOutToProperty; - QMap m_kerningToProperty; - - bool m_settingValue; - QTimer *m_fontDatabaseChangeTimer; -}; - -QtFontPropertyManagerPrivate::QtFontPropertyManagerPrivate() : - m_settingValue(false), - m_fontDatabaseChangeTimer(0) -{ -} - -void QtFontPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) -{ - if (m_settingValue) - return; - if (QtProperty *prop = m_pixelSizeToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setPixelSize(value); - q_ptr->setValue(prop, f); - } -} - -void QtFontPropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) -{ - if (m_settingValue) - return; - if (QtProperty *prop = m_familyToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setFamily(m_familyNames.at(value)); - q_ptr->setValue(prop, f); - } -} - -void QtFontPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) -{ - if (m_settingValue) - return; - if (QtProperty *prop = m_boldToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setBold(value); - q_ptr->setValue(prop, f); - } else if (QtProperty *prop = m_italicToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setItalic(value); - q_ptr->setValue(prop, f); - } else if (QtProperty *prop = m_underlineToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setUnderline(value); - q_ptr->setValue(prop, f); - } else if (QtProperty *prop = m_strikeOutToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setStrikeOut(value); - q_ptr->setValue(prop, f); - } else if (QtProperty *prop = m_kerningToProperty.value(property, 0)) { - QFont f = m_values[prop]; - f.setKerning(value); - q_ptr->setValue(prop, f); - } -} - -void QtFontPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pixelProp = m_pixelSizeToProperty.value(property, 0)) { - m_propertyToPixelSize[pixelProp] = 0; - m_pixelSizeToProperty.remove(property); - } else if (QtProperty *pointProp = m_familyToProperty.value(property, 0)) { - m_propertyToFamily[pointProp] = 0; - m_familyToProperty.remove(property); - } else if (QtProperty *pointProp = m_boldToProperty.value(property, 0)) { - m_propertyToBold[pointProp] = 0; - m_boldToProperty.remove(property); - } else if (QtProperty *pointProp = m_italicToProperty.value(property, 0)) { - m_propertyToItalic[pointProp] = 0; - m_italicToProperty.remove(property); - } else if (QtProperty *pointProp = m_underlineToProperty.value(property, 0)) { - m_propertyToUnderline[pointProp] = 0; - m_underlineToProperty.remove(property); - } else if (QtProperty *pointProp = m_strikeOutToProperty.value(property, 0)) { - m_propertyToStrikeOut[pointProp] = 0; - m_strikeOutToProperty.remove(property); - } else if (QtProperty *pointProp = m_kerningToProperty.value(property, 0)) { - m_propertyToKerning[pointProp] = 0; - m_kerningToProperty.remove(property); - } -} - -void QtFontPropertyManagerPrivate::slotFontDatabaseChanged() -{ - if (!m_fontDatabaseChangeTimer) { - m_fontDatabaseChangeTimer = new QTimer(q_ptr); - m_fontDatabaseChangeTimer->setInterval(0); - m_fontDatabaseChangeTimer->setSingleShot(true); - QObject::connect(m_fontDatabaseChangeTimer, SIGNAL(timeout()), q_ptr, SLOT(slotFontDatabaseDelayedChange())); - } - if (!m_fontDatabaseChangeTimer->isActive()) - m_fontDatabaseChangeTimer->start(); -} - -void QtFontPropertyManagerPrivate::slotFontDatabaseDelayedChange() -{ - typedef QMap PropertyPropertyMap; - // rescan available font names - const QStringList oldFamilies = m_familyNames; - m_familyNames = fontDatabase()->families(); - - // Adapt all existing properties - if (!m_propertyToFamily.empty()) { - PropertyPropertyMap::const_iterator cend = m_propertyToFamily.constEnd(); - for (PropertyPropertyMap::const_iterator it = m_propertyToFamily.constBegin(); it != cend; ++it) { - QtProperty *familyProp = it.value(); - const int oldIdx = m_enumPropertyManager->value(familyProp); - int newIdx = m_familyNames.indexOf(oldFamilies.at(oldIdx)); - if (newIdx < 0) - newIdx = 0; - m_enumPropertyManager->setEnumNames(familyProp, m_familyNames); - m_enumPropertyManager->setValue(familyProp, newIdx); - } - } -} - -/*! - \class QtFontPropertyManager - - \brief The QtFontPropertyManager provides and manages QFont properties. - - A font property has nested \e family, \e pointSize, \e bold, \e - italic, \e underline, \e strikeOut and \e kerning subproperties. The top-level - property's value can be retrieved using the value() function, and - set using the setValue() slot. - - The subproperties are created by QtIntPropertyManager, QtEnumPropertyManager and - QtBoolPropertyManager objects. These managers can be retrieved using the - corresponding subIntPropertyManager(), subEnumPropertyManager() and - subBoolPropertyManager() functions. In order to provide editing widgets - for the subproperties in a property browser widget, these managers - must be associated with editor factories. - - In addition, QtFontPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager, QtEnumPropertyManager, QtIntPropertyManager, QtBoolPropertyManager -*/ - -/*! - \fn void QtFontPropertyManager::valueChanged(QtProperty *property, const QFont &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtFontPropertyManager::QtFontPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtFontPropertyManagerPrivate; - d_ptr->q_ptr = this; -#if QT_VERSION >= 0x040500 - QObject::connect(qApp, SIGNAL(fontDatabaseChanged()), this, SLOT(slotFontDatabaseChanged())); -#endif - - d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); - connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotIntChanged(QtProperty *, int))); - d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); - connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotEnumChanged(QtProperty *, int))); - d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); - connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotBoolChanged(QtProperty *, bool))); - - connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); - connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); - connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtFontPropertyManager::~QtFontPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that creates the \e pointSize subproperty. - - In order to provide editing widgets for the \e pointSize property - in a property browser widget, this manager must be associated - with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtIntPropertyManager *QtFontPropertyManager::subIntPropertyManager() const -{ - return d_ptr->m_intPropertyManager; -} - -/*! - Returns the manager that create the \e family subproperty. - - In order to provide editing widgets for the \e family property - in a property browser widget, this manager must be associated - with an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtEnumPropertyManager *QtFontPropertyManager::subEnumPropertyManager() const -{ - return d_ptr->m_enumPropertyManager; -} - -/*! - Returns the manager that creates the \e bold, \e italic, \e underline, - \e strikeOut and \e kerning subproperties. - - In order to provide editing widgets for the mentioned properties - in a property browser widget, this manager must be associated with - an editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtBoolPropertyManager *QtFontPropertyManager::subBoolPropertyManager() const -{ - return d_ptr->m_boolPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given property is not managed by this manager, this - function returns a font object that uses the application's default - font. - - \sa setValue() -*/ -QFont QtFontPropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QFont()); -} - -/*! - \reimp -*/ -QString QtFontPropertyManager::valueText(const QtProperty *property) const -{ - const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - return QtPropertyBrowserUtils::fontValueText(it.value()); -} - -/*! - \reimp -*/ -QIcon QtFontPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QIcon(); - - return QtPropertyBrowserUtils::fontValueIcon(it.value()); -} - -/*! - \fn void QtFontPropertyManager::setValue(QtProperty *property, const QFont &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - \sa value(), valueChanged() -*/ -void QtFontPropertyManager::setValue(QtProperty *property, const QFont &val) -{ - const QtFontPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - const QFont oldVal = it.value(); -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - if (oldVal == val && oldVal.resolve() == val.resolve()) -#else - if (oldVal == val && oldVal.resolveMask() == val.resolveMask()) -#endif - return; - - it.value() = val; - - int idx = d_ptr->m_familyNames.indexOf(val.family()); - if (idx == -1) - idx = 0; - bool settingValue = d_ptr->m_settingValue; - d_ptr->m_settingValue = true; - d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToFamily[property], idx); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToPixelSize[property], val.pixelSize()); - d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToBold[property], val.bold()); - d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToItalic[property], val.italic()); - d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToUnderline[property], val.underline()); - d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToStrikeOut[property], val.strikeOut()); - d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToKerning[property], val.kerning()); - d_ptr->m_settingValue = settingValue; - - emit propertyChanged(property); - emit valueChanged(property, val); -} - -/*! - \reimp -*/ -void QtFontPropertyManager::initializeProperty(QtProperty *property) -{ - QFont val; - d_ptr->m_values[property] = val; - - QtProperty *familyProp = d_ptr->m_enumPropertyManager->addProperty(); - familyProp->setPropertyName(tr("Family")); - if (d_ptr->m_familyNames.empty()) - d_ptr->m_familyNames = fontDatabase()->families(); - d_ptr->m_enumPropertyManager->setEnumNames(familyProp, d_ptr->m_familyNames); - int idx = d_ptr->m_familyNames.indexOf(val.family()); - if (idx == -1) - idx = 0; - d_ptr->m_enumPropertyManager->setValue(familyProp, idx); - d_ptr->m_propertyToFamily[property] = familyProp; - d_ptr->m_familyToProperty[familyProp] = property; - property->addSubProperty(familyProp); - - QtProperty *pixelSizeProp = d_ptr->m_intPropertyManager->addProperty(); - pixelSizeProp->setPropertyName(tr("Pixel Size")); - d_ptr->m_intPropertyManager->setValue(pixelSizeProp, val.pointSize()); - d_ptr->m_intPropertyManager->setMinimum(pixelSizeProp, 1); - d_ptr->m_propertyToPixelSize[property] = pixelSizeProp; - d_ptr->m_pixelSizeToProperty[pixelSizeProp] = property; - property->addSubProperty(pixelSizeProp); - - QtProperty *boldProp = d_ptr->m_boolPropertyManager->addProperty(); - boldProp->setPropertyName(tr("Bold")); - d_ptr->m_boolPropertyManager->setValue(boldProp, val.bold()); - d_ptr->m_propertyToBold[property] = boldProp; - d_ptr->m_boldToProperty[boldProp] = property; - property->addSubProperty(boldProp); - - QtProperty *italicProp = d_ptr->m_boolPropertyManager->addProperty(); - italicProp->setPropertyName(tr("Italic")); - d_ptr->m_boolPropertyManager->setValue(italicProp, val.italic()); - d_ptr->m_propertyToItalic[property] = italicProp; - d_ptr->m_italicToProperty[italicProp] = property; - property->addSubProperty(italicProp); - - QtProperty *underlineProp = d_ptr->m_boolPropertyManager->addProperty(); - underlineProp->setPropertyName(tr("Underline")); - d_ptr->m_boolPropertyManager->setValue(underlineProp, val.underline()); - d_ptr->m_propertyToUnderline[property] = underlineProp; - d_ptr->m_underlineToProperty[underlineProp] = property; - property->addSubProperty(underlineProp); - - QtProperty *strikeOutProp = d_ptr->m_boolPropertyManager->addProperty(); - strikeOutProp->setPropertyName(tr("Strikeout")); - d_ptr->m_boolPropertyManager->setValue(strikeOutProp, val.strikeOut()); - d_ptr->m_propertyToStrikeOut[property] = strikeOutProp; - d_ptr->m_strikeOutToProperty[strikeOutProp] = property; - property->addSubProperty(strikeOutProp); - - QtProperty *kerningProp = d_ptr->m_boolPropertyManager->addProperty(); - kerningProp->setPropertyName(tr("Kerning")); - d_ptr->m_boolPropertyManager->setValue(kerningProp, val.kerning()); - d_ptr->m_propertyToKerning[property] = kerningProp; - d_ptr->m_kerningToProperty[kerningProp] = property; - property->addSubProperty(kerningProp); -} - -/*! - \reimp -*/ -void QtFontPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *familyProp = d_ptr->m_propertyToFamily[property]; - if (familyProp) { - d_ptr->m_familyToProperty.remove(familyProp); - delete familyProp; - } - d_ptr->m_propertyToFamily.remove(property); - - QtProperty *pixelSizeProp = d_ptr->m_propertyToPixelSize[property]; - if (pixelSizeProp) { - d_ptr->m_pixelSizeToProperty.remove(pixelSizeProp); - delete pixelSizeProp; - } - d_ptr->m_propertyToPixelSize.remove(property); - - QtProperty *boldProp = d_ptr->m_propertyToBold[property]; - if (boldProp) { - d_ptr->m_boldToProperty.remove(boldProp); - delete boldProp; - } - d_ptr->m_propertyToBold.remove(property); - - QtProperty *italicProp = d_ptr->m_propertyToItalic[property]; - if (italicProp) { - d_ptr->m_italicToProperty.remove(italicProp); - delete italicProp; - } - d_ptr->m_propertyToItalic.remove(property); - - QtProperty *underlineProp = d_ptr->m_propertyToUnderline[property]; - if (underlineProp) { - d_ptr->m_underlineToProperty.remove(underlineProp); - delete underlineProp; - } - d_ptr->m_propertyToUnderline.remove(property); - - QtProperty *strikeOutProp = d_ptr->m_propertyToStrikeOut[property]; - if (strikeOutProp) { - d_ptr->m_strikeOutToProperty.remove(strikeOutProp); - delete strikeOutProp; - } - d_ptr->m_propertyToStrikeOut.remove(property); - - QtProperty *kerningProp = d_ptr->m_propertyToKerning[property]; - if (kerningProp) { - d_ptr->m_kerningToProperty.remove(kerningProp); - delete kerningProp; - } - d_ptr->m_propertyToKerning.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtColorPropertyManager - -class QtColorPropertyManagerPrivate -{ - QtColorPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtColorPropertyManager) -public: - - void slotIntChanged(QtProperty *property, int value); - void slotPropertyDestroyed(QtProperty *property); - - typedef QMap PropertyValueMap; - PropertyValueMap m_values; - - QtIntPropertyManager *m_intPropertyManager; - bool m_applyingValueChange; - - QMap m_propertyToR; - QMap m_propertyToG; - QMap m_propertyToB; - QMap m_propertyToA; - - QMap m_rToProperty; - QMap m_gToProperty; - QMap m_bToProperty; - QMap m_aToProperty; -}; - -void QtColorPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) -{ - if (m_applyingValueChange) - return; - - if (QtProperty *prop = m_rToProperty.value(property, 0)) { - QColor c = m_values[prop]; - c.setRed(value); - q_ptr->setValue(prop, c); - } else if (QtProperty *prop = m_gToProperty.value(property, 0)) { - QColor c = m_values[prop]; - c.setGreen(value); - q_ptr->setValue(prop, c); - } else if (QtProperty *prop = m_bToProperty.value(property, 0)) { - QColor c = m_values[prop]; - c.setBlue(value); - q_ptr->setValue(prop, c); - } else if (QtProperty *prop = m_aToProperty.value(property, 0)) { - QColor c = m_values[prop]; - c.setAlpha(value); - q_ptr->setValue(prop, c); - } -} - -void QtColorPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) -{ - if (QtProperty *pointProp = m_rToProperty.value(property, 0)) { - m_propertyToR[pointProp] = 0; - m_rToProperty.remove(property); - } else if (QtProperty *pointProp = m_gToProperty.value(property, 0)) { - m_propertyToG[pointProp] = 0; - m_gToProperty.remove(property); - } else if (QtProperty *pointProp = m_bToProperty.value(property, 0)) { - m_propertyToB[pointProp] = 0; - m_bToProperty.remove(property); - } else if (QtProperty *pointProp = m_aToProperty.value(property, 0)) { - m_propertyToA[pointProp] = 0; - m_aToProperty.remove(property); - } -} - -/*! - \class QtColorPropertyManager - - \brief The QtColorPropertyManager provides and manages QColor properties. - - A color property has nested \e red, \e green and \e blue - subproperties. The top-level property's value can be retrieved - using the value() function, and set using the setValue() slot. - - The subproperties are created by a QtIntPropertyManager object. This - manager can be retrieved using the subIntPropertyManager() function. In - order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - In addition, QtColorPropertyManager provides the valueChanged() signal - which is emitted whenever a property created by this manager - changes. - - \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser, QtIntPropertyManager -*/ - -/*! - \fn void QtColorPropertyManager::valueChanged(QtProperty *property, const QColor &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtColorPropertyManager::QtColorPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtColorPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); - d_ptr->m_applyingValueChange = false; - - connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotIntChanged(QtProperty *, int))); - - connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), - this, SLOT(slotPropertyDestroyed(QtProperty *))); -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtColorPropertyManager::~QtColorPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the manager that produces the nested \e red, \e green and - \e blue subproperties. - - In order to provide editing widgets for the subproperties in a - property browser widget, this manager must be associated with an - editor factory. - - \sa QtAbstractPropertyBrowser::setFactoryForManager() -*/ -QtIntPropertyManager *QtColorPropertyManager::subIntPropertyManager() const -{ - return d_ptr->m_intPropertyManager; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by \e this manager, this - function returns an invalid color. - - \sa setValue() -*/ -QColor QtColorPropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QColor()); -} - -/*! - \reimp -*/ - -QString QtColorPropertyManager::valueText(const QtProperty *property) const -{ - const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - return QtPropertyBrowserUtils::colorValueText(it.value()); -} - -/*! - \reimp -*/ - -QIcon QtColorPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QIcon(); - if (!it.value().isValid()) - return QIcon(); - return QtPropertyBrowserUtils::brushValueIcon(QBrush(it.value())); -} - -/*! - \fn void QtColorPropertyManager::setValue(QtProperty *property, const QColor &value) - - Sets the value of the given \a property to \a value. Nested - properties are updated automatically. - - \sa value(), valueChanged() -*/ -void QtColorPropertyManager::setValue(QtProperty *property, const QColor &val) -{ - const QtColorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - if (it.value() == val) - return; - - it.value() = val; - - d_ptr->m_applyingValueChange = true; - - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToR[property], val.red()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToG[property], val.green()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToB[property], val.blue()); - d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToA[property], val.alpha()); - - d_ptr->m_applyingValueChange = false; - - emit propertyChanged(property); - emit valueChanged(property, val); -} - -/*! - \reimp -*/ -void QtColorPropertyManager::initializeProperty(QtProperty *property) -{ - QColor val; - d_ptr->m_values[property] = val; - - QtProperty *rProp = d_ptr->m_intPropertyManager->addProperty(); - rProp->setPropertyName(tr("Red")); - d_ptr->m_intPropertyManager->setValue(rProp, val.red()); - d_ptr->m_intPropertyManager->setRange(rProp, 0, 0xFF); - d_ptr->m_propertyToR[property] = rProp; - d_ptr->m_rToProperty[rProp] = property; - property->addSubProperty(rProp); - - QtProperty *gProp = d_ptr->m_intPropertyManager->addProperty(); - gProp->setPropertyName(tr("Green")); - d_ptr->m_intPropertyManager->setValue(gProp, val.green()); - d_ptr->m_intPropertyManager->setRange(gProp, 0, 0xFF); - d_ptr->m_propertyToG[property] = gProp; - d_ptr->m_gToProperty[gProp] = property; - property->addSubProperty(gProp); - - QtProperty *bProp = d_ptr->m_intPropertyManager->addProperty(); - bProp->setPropertyName(tr("Blue")); - d_ptr->m_intPropertyManager->setValue(bProp, val.blue()); - d_ptr->m_intPropertyManager->setRange(bProp, 0, 0xFF); - d_ptr->m_propertyToB[property] = bProp; - d_ptr->m_bToProperty[bProp] = property; - property->addSubProperty(bProp); - - QtProperty *aProp = d_ptr->m_intPropertyManager->addProperty(); - aProp->setPropertyName(tr("Alpha")); - d_ptr->m_intPropertyManager->setValue(aProp, val.alpha()); - d_ptr->m_intPropertyManager->setRange(aProp, 0, 0xFF); - d_ptr->m_propertyToA[property] = aProp; - d_ptr->m_aToProperty[aProp] = property; - property->addSubProperty(aProp); -} - -/*! - \reimp -*/ -void QtColorPropertyManager::uninitializeProperty(QtProperty *property) -{ - QtProperty *rProp = d_ptr->m_propertyToR[property]; - if (rProp) { - d_ptr->m_rToProperty.remove(rProp); - delete rProp; - } - d_ptr->m_propertyToR.remove(property); - - QtProperty *gProp = d_ptr->m_propertyToG[property]; - if (gProp) { - d_ptr->m_gToProperty.remove(gProp); - delete gProp; - } - d_ptr->m_propertyToG.remove(property); - - QtProperty *bProp = d_ptr->m_propertyToB[property]; - if (bProp) { - d_ptr->m_bToProperty.remove(bProp); - delete bProp; - } - d_ptr->m_propertyToB.remove(property); - - QtProperty *aProp = d_ptr->m_propertyToA[property]; - if (aProp) { - d_ptr->m_aToProperty.remove(aProp); - delete aProp; - } - d_ptr->m_propertyToA.remove(property); - - d_ptr->m_values.remove(property); -} - -// QtCursorPropertyManager - -// Make sure icons are removed as soon as QApplication is destroyed, otherwise, -// handles are leaked on X11. -static void clearCursorDatabase(); -namespace { -struct CursorDatabase : public QtCursorDatabase -{ - CursorDatabase() - { - qAddPostRoutine(clearCursorDatabase); - } -}; -} -Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) - -static void clearCursorDatabase() -{ - cursorDatabase()->clear(); -} - -class QtCursorPropertyManagerPrivate -{ - QtCursorPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtCursorPropertyManager) -public: - typedef QMap PropertyValueMap; - PropertyValueMap m_values; -}; - -/*! - \class QtCursorPropertyManager - - \brief The QtCursorPropertyManager provides and manages QCursor properties. - - A cursor property has a current value which can be - retrieved using the value() function, and set using the setValue() - slot. In addition, QtCursorPropertyManager provides the - valueChanged() signal which is emitted whenever a property created - by this manager changes. - - \sa QtAbstractPropertyManager -*/ - -/*! - \fn void QtCursorPropertyManager::valueChanged(QtProperty *property, const QCursor &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the new - \a value as parameters. - - \sa setValue() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtCursorPropertyManager::QtCursorPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtCursorPropertyManagerPrivate; - d_ptr->q_ptr = this; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtCursorPropertyManager::~QtCursorPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns a default QCursor object. - - \sa setValue() -*/ -#ifndef QT_NO_CURSOR -QCursor QtCursorPropertyManager::value(const QtProperty *property) const -{ - return d_ptr->m_values.value(property, QCursor()); -} -#endif - -/*! - \reimp -*/ -QString QtCursorPropertyManager::valueText(const QtProperty *property) const -{ - const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QString(); - - return cursorDatabase()->cursorToShapeName(it.value()); -} - -/*! - \reimp -*/ -QIcon QtCursorPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); - if (it == d_ptr->m_values.constEnd()) - return QIcon(); - - return cursorDatabase()->cursorToShapeIcon(it.value()); -} - -/*! - \fn void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) - - Sets the value of the given \a property to \a value. - - \sa value(), valueChanged() -*/ -void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) -{ -#ifndef QT_NO_CURSOR - const QtCursorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; - - if (it.value().shape() == value.shape() && value.shape() != Qt::BitmapCursor) - return; - - it.value() = value; - - emit propertyChanged(property); - emit valueChanged(property, value); -#endif -} - -/*! - \reimp -*/ -void QtCursorPropertyManager::initializeProperty(QtProperty *property) -{ -#ifndef QT_NO_CURSOR - d_ptr->m_values[property] = QCursor(); -#endif -} - -/*! - \reimp -*/ -void QtCursorPropertyManager::uninitializeProperty(QtProperty *property) -{ - d_ptr->m_values.remove(property); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qtpropertymanager.cpp" -#include "qtpropertymanager.moc" diff --git a/src/qtpropertybrowser/src/qtpropertymanager.h b/src/qtpropertybrowser/src/qtpropertymanager.h deleted file mode 100644 index f2fa06089d..0000000000 --- a/src/qtpropertybrowser/src/qtpropertymanager.h +++ /dev/null @@ -1,714 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTPROPERTYMANAGER_H -#define QTPROPERTYMANAGER_H - -#include "qtpropertybrowser.h" -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QDate; -class QTime; -class QDateTime; -class QLocale; - -class QT_QTPROPERTYBROWSER_EXPORT QtGroupPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtGroupPropertyManager(QObject *parent = 0); - ~QtGroupPropertyManager(); - -protected: - virtual bool hasValue(const QtProperty *property) const; - - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -}; - -class QtIntPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtIntPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtIntPropertyManager(QObject *parent = 0); - ~QtIntPropertyManager(); - - int value(const QtProperty *property) const; - int minimum(const QtProperty *property) const; - int maximum(const QtProperty *property) const; - int singleStep(const QtProperty *property) const; - bool isReadOnly(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, int val); - void setMinimum(QtProperty *property, int minVal); - void setMaximum(QtProperty *property, int maxVal); - void setRange(QtProperty *property, int minVal, int maxVal); - void setSingleStep(QtProperty *property, int step); - void setReadOnly(QtProperty *property, bool readOnly); -Q_SIGNALS: - void valueChanged(QtProperty *property, int val); - void rangeChanged(QtProperty *property, int minVal, int maxVal); - void singleStepChanged(QtProperty *property, int step); - void readOnlyChanged(QtProperty *property, bool readOnly); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtIntPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtIntPropertyManager) - Q_DISABLE_COPY(QtIntPropertyManager) -}; - -class QtBoolPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtBoolPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtBoolPropertyManager(QObject *parent = 0); - ~QtBoolPropertyManager(); - - bool value(const QtProperty *property) const; - bool textVisible(const QtProperty *property) const; - - static void resetIcons(); - -public Q_SLOTS: - void setValue(QtProperty *property, bool val); - void setTextVisible(QtProperty *property, bool textVisible); -Q_SIGNALS: - void valueChanged(QtProperty *property, bool val); - void textVisibleChanged(QtProperty *property, bool); -protected: - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtBoolPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtBoolPropertyManager) - Q_DISABLE_COPY(QtBoolPropertyManager) -}; - -class QtDoublePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDoublePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtDoublePropertyManager(QObject *parent = 0); - ~QtDoublePropertyManager(); - - double value(const QtProperty *property) const; - double minimum(const QtProperty *property) const; - double maximum(const QtProperty *property) const; - double singleStep(const QtProperty *property) const; - int decimals(const QtProperty *property) const; - bool isReadOnly(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, double val); - void setMinimum(QtProperty *property, double minVal); - void setMaximum(QtProperty *property, double maxVal); - void setRange(QtProperty *property, double minVal, double maxVal); - void setSingleStep(QtProperty *property, double step); - void setDecimals(QtProperty *property, int prec); - void setReadOnly(QtProperty *property, bool readOnly); -Q_SIGNALS: - void valueChanged(QtProperty *property, double val); - void rangeChanged(QtProperty *property, double minVal, double maxVal); - void singleStepChanged(QtProperty *property, double step); - void decimalsChanged(QtProperty *property, int prec); - void readOnlyChanged(QtProperty *property, bool readOnly); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtDoublePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDoublePropertyManager) - Q_DISABLE_COPY(QtDoublePropertyManager) -}; - -class QtStringPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtStringPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtStringPropertyManager(QObject *parent = 0); - ~QtStringPropertyManager(); - - QString value(const QtProperty *property) const; - QRegularExpression regExp(const QtProperty *property) const; - EchoMode echoMode(const QtProperty *property) const; - bool isReadOnly(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QString &val); - void setRegExp(QtProperty *property, const QRegularExpression ®Exp); - void setEchoMode(QtProperty *property, EchoMode echoMode); - void setReadOnly(QtProperty *property, bool readOnly); - -Q_SIGNALS: - void valueChanged(QtProperty *property, const QString &val); - void regExpChanged(QtProperty *property, const QRegularExpression ®Exp); - void echoModeChanged(QtProperty *property, const int); - void readOnlyChanged(QtProperty *property, bool); - -protected: - QString valueText(const QtProperty *property) const; - QString displayText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtStringPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtStringPropertyManager) - Q_DISABLE_COPY(QtStringPropertyManager) -}; - -class QtDatePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDatePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtDatePropertyManager(QObject *parent = 0); - ~QtDatePropertyManager(); - - QDate value(const QtProperty *property) const; - QDate minimum(const QtProperty *property) const; - QDate maximum(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QDate &val); - void setMinimum(QtProperty *property, const QDate &minVal); - void setMaximum(QtProperty *property, const QDate &maxVal); - void setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QDate &val); - void rangeChanged(QtProperty *property, const QDate &minVal, const QDate &maxVal); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtDatePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDatePropertyManager) - Q_DISABLE_COPY(QtDatePropertyManager) -}; - -class QtTimePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtTimePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtTimePropertyManager(QObject *parent = 0); - ~QtTimePropertyManager(); - - QTime value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QTime &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QTime &val); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtTimePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtTimePropertyManager) - Q_DISABLE_COPY(QtTimePropertyManager) -}; - -class QtDateTimePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtDateTimePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtDateTimePropertyManager(QObject *parent = 0); - ~QtDateTimePropertyManager(); - - QDateTime value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QDateTime &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QDateTime &val); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtDateTimePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtDateTimePropertyManager) - Q_DISABLE_COPY(QtDateTimePropertyManager) -}; - -class QtKeySequencePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtKeySequencePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtKeySequencePropertyManager(QObject *parent = 0); - ~QtKeySequencePropertyManager(); - - QKeySequence value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QKeySequence &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QKeySequence &val); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtKeySequencePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtKeySequencePropertyManager) - Q_DISABLE_COPY(QtKeySequencePropertyManager) -}; - -class QtCharPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtCharPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtCharPropertyManager(QObject *parent = 0); - ~QtCharPropertyManager(); - - QChar value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QChar &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QChar &val); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtCharPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtCharPropertyManager) - Q_DISABLE_COPY(QtCharPropertyManager) -}; - -class QtPointPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtPointPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtPointPropertyManager(QObject *parent = 0); - ~QtPointPropertyManager(); - - QtIntPropertyManager *subIntPropertyManager() const; - - QPoint value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QPoint &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QPoint &val); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtPointPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtPointPropertyManager) - Q_DISABLE_COPY(QtPointPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtPointFPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtPointFPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtPointFPropertyManager(QObject *parent = 0); - ~QtPointFPropertyManager(); - - QtDoublePropertyManager *subDoublePropertyManager() const; - - QPointF value(const QtProperty *property) const; - double singleStep(const QtProperty *property) const; - int decimals(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QPointF &val); - void setSingleStep(QtProperty *property, double step); - void setDecimals(QtProperty *property, int prec); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QPointF &val); - void singleStepChanged(QtProperty *property, double step); - void decimalsChanged(QtProperty *property, int prec); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtPointFPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtPointFPropertyManager) - Q_DISABLE_COPY(QtPointFPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtSizePropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtSizePropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtSizePropertyManager(QObject *parent = 0); - ~QtSizePropertyManager(); - - QtIntPropertyManager *subIntPropertyManager() const; - - QSize value(const QtProperty *property) const; - QSize minimum(const QtProperty *property) const; - QSize maximum(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QSize &val); - void setMinimum(QtProperty *property, const QSize &minVal); - void setMaximum(QtProperty *property, const QSize &maxVal); - void setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QSize &val); - void rangeChanged(QtProperty *property, const QSize &minVal, const QSize &maxVal); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtSizePropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtSizePropertyManager) - Q_DISABLE_COPY(QtSizePropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtSizeFPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtSizeFPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtSizeFPropertyManager(QObject *parent = 0); - ~QtSizeFPropertyManager(); - - QtDoublePropertyManager *subDoublePropertyManager() const; - - QSizeF value(const QtProperty *property) const; - QSizeF minimum(const QtProperty *property) const; - QSizeF maximum(const QtProperty *property) const; - int decimals(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QSizeF &val); - void setMinimum(QtProperty *property, const QSizeF &minVal); - void setMaximum(QtProperty *property, const QSizeF &maxVal); - void setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); - void setDecimals(QtProperty *property, int prec); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QSizeF &val); - void rangeChanged(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); - void decimalsChanged(QtProperty *property, int prec); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtSizeFPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtSizeFPropertyManager) - Q_DISABLE_COPY(QtSizeFPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtRectPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtRectPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtRectPropertyManager(QObject *parent = 0); - ~QtRectPropertyManager(); - - QtIntPropertyManager *subIntPropertyManager() const; - - QRect value(const QtProperty *property) const; - QRect constraint(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QRect &val); - void setConstraint(QtProperty *property, const QRect &constraint); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QRect &val); - void constraintChanged(QtProperty *property, const QRect &constraint); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtRectPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtRectPropertyManager) - Q_DISABLE_COPY(QtRectPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtRectFPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtRectFPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtRectFPropertyManager(QObject *parent = 0); - ~QtRectFPropertyManager(); - - QtDoublePropertyManager *subDoublePropertyManager() const; - - QRectF value(const QtProperty *property) const; - QRectF constraint(const QtProperty *property) const; - int decimals(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QRectF &val); - void setConstraint(QtProperty *property, const QRectF &constraint); - void setDecimals(QtProperty *property, int prec); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QRectF &val); - void constraintChanged(QtProperty *property, const QRectF &constraint); - void decimalsChanged(QtProperty *property, int prec); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtRectFPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtRectFPropertyManager) - Q_DISABLE_COPY(QtRectFPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtEnumPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtEnumPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtEnumPropertyManager(QObject *parent = 0); - ~QtEnumPropertyManager(); - - int value(const QtProperty *property) const; - QStringList enumNames(const QtProperty *property) const; - QMap enumIcons(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, int val); - void setEnumNames(QtProperty *property, const QStringList &names); - void setEnumIcons(QtProperty *property, const QMap &icons); -Q_SIGNALS: - void valueChanged(QtProperty *property, int val); - void enumNamesChanged(QtProperty *property, const QStringList &names); - void enumIconsChanged(QtProperty *property, const QMap &icons); -protected: - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtEnumPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtEnumPropertyManager) - Q_DISABLE_COPY(QtEnumPropertyManager) -}; - -class QtFlagPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtFlagPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtFlagPropertyManager(QObject *parent = 0); - ~QtFlagPropertyManager(); - - QtBoolPropertyManager *subBoolPropertyManager() const; - - int value(const QtProperty *property) const; - QStringList flagNames(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, int val); - void setFlagNames(QtProperty *property, const QStringList &names); -Q_SIGNALS: - void valueChanged(QtProperty *property, int val); - void flagNamesChanged(QtProperty *property, const QStringList &names); -protected: - QString valueText(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtFlagPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtFlagPropertyManager) - Q_DISABLE_COPY(QtFlagPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtFontPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtFontPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtFontPropertyManager(QObject *parent = 0); - ~QtFontPropertyManager(); - - QtIntPropertyManager *subIntPropertyManager() const; - QtEnumPropertyManager *subEnumPropertyManager() const; - QtBoolPropertyManager *subBoolPropertyManager() const; - - QFont value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QFont &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QFont &val); -protected: - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtFontPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtFontPropertyManager) - Q_DISABLE_COPY(QtFontPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) - Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseChanged()) - Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseDelayedChange()) -}; - -class QtColorPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtColorPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtColorPropertyManager(QObject *parent = 0); - ~QtColorPropertyManager(); - - QtIntPropertyManager *subIntPropertyManager() const; - - QColor value(const QtProperty *property) const; - -public Q_SLOTS: - void setValue(QtProperty *property, const QColor &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QColor &val); -protected: - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtColorPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtColorPropertyManager) - Q_DISABLE_COPY(QtColorPropertyManager) - Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) -}; - -class QtCursorPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtCursorPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtCursorPropertyManager(QObject *parent = 0); - ~QtCursorPropertyManager(); - -#ifndef QT_NO_CURSOR - QCursor value(const QtProperty *property) const; -#endif - -public Q_SLOTS: - void setValue(QtProperty *property, const QCursor &val); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QCursor &val); -protected: - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); -private: - QtCursorPropertyManagerPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtCursorPropertyManager) - Q_DISABLE_COPY(QtCursorPropertyManager) -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qttreepropertybrowser.cpp b/src/qtpropertybrowser/src/qttreepropertybrowser.cpp deleted file mode 100644 index bb928c6510..0000000000 --- a/src/qtpropertybrowser/src/qttreepropertybrowser.cpp +++ /dev/null @@ -1,1200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qttreepropertybrowser.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_OS_MAC -static qreal defaultDpiScale() -{ - if (const QScreen *screen = QGuiApplication::primaryScreen()) - return screen->logicalDotsPerInchX() / 96.0; - return 1.0; -} -#endif - -static qreal dpiScaled(qreal value) -{ -#ifdef Q_OS_MAC - // On mac the DPI is always 72 so we should not scale it - return value; -#else - static const qreal scale = defaultDpiScale(); - return value * scale; -#endif -} - -static QSize dpiScaled(QSize value) -{ - return QSize(qRound(dpiScaled(value.width())), - qRound(dpiScaled(value.height()))); -} - - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtPropertyEditorView; - -class QtTreePropertyBrowserPrivate -{ - QtTreePropertyBrowser *q_ptr; - Q_DECLARE_PUBLIC(QtTreePropertyBrowser) - -public: - QtTreePropertyBrowserPrivate(); - void init(QWidget *parent); - - void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); - void propertyRemoved(QtBrowserItem *index); - void propertyChanged(QtBrowserItem *index); - QWidget *createEditor(QtProperty *property, QWidget *parent) const - { return q_ptr->createEditor(property, parent); } - QtProperty *indexToProperty(const QModelIndex &index) const; - QTreeWidgetItem *indexToItem(const QModelIndex &index) const; - QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const; - bool lastColumn(int column) const; - void disableItem(QTreeWidgetItem *item) const; - void enableItem(QTreeWidgetItem *item) const; - bool hasValue(QTreeWidgetItem *item) const; - - void slotCollapsed(const QModelIndex &index); - void slotExpanded(const QModelIndex &index); - - QColor calculatedBackgroundColor(QtBrowserItem *item) const; - - QtPropertyEditorView *treeWidget() const { return m_treeWidget; } - bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; } - - QtBrowserItem *currentItem() const; - void setCurrentItem(QtBrowserItem *browserItem, bool block); - void editItem(QtBrowserItem *browserItem); - - QList selectedItems() const; - - void slotCurrentBrowserItemChanged(QtBrowserItem *item); - void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *); - void slotItemSelectionChanged(); - - QTreeWidgetItem *editedItem() const; - QtBrowserItem *editedBrowserItem() const; - -private: - void updateItem(QTreeWidgetItem *item); - - QMap m_indexToItem; - QMap m_itemToIndex; - - QMap m_indexToBackgroundColor; - - QtPropertyEditorView *m_treeWidget; - - bool m_headerVisible; - bool m_allowMultiSelection; - QtTreePropertyBrowser::ResizeMode m_resizeMode; - class QtPropertyEditorDelegate *m_delegate; - bool m_markPropertiesWithoutValue; - bool m_browserChangedBlocked; - QIcon m_expandIcon; -}; - -// ------------ QtPropertyEditorView -class QtPropertyEditorView : public QTreeWidget -{ - Q_OBJECT -public: - explicit QtPropertyEditorView(QWidget *parent = 0); - - void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) - { m_editorPrivate = editorPrivate; } - - QTreeWidgetItem *indexToItem(const QModelIndex &index) const - { return itemFromIndex(index); } - -protected: - void keyPressEvent(QKeyEvent *event); - void mousePressEvent(QMouseEvent *event); - void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - -private: - QtTreePropertyBrowserPrivate *m_editorPrivate; -}; - -QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) : - QTreeWidget(parent), - m_editorPrivate(0) -{ - setUniformRowHeights(true); - connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int))); -} - -void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - bool hasValue = true; - if (m_editorPrivate) { - QtProperty *property = m_editorPrivate->indexToProperty(index); - if (property) - hasValue = property->hasValue(); - } - if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { - const QColor c = option.palette.color(QPalette::Dark); - painter->fillRect(option.rect, c); - opt.palette.setColor(QPalette::AlternateBase, c); - } else { - const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); - if (c.isValid()) { - painter->fillRect(option.rect, c); - opt.palette.setColor(QPalette::AlternateBase, c.lighter(112)); - } - } - QTreeWidget::drawRow(painter, opt, index); - QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); - painter->save(); - painter->setPen(QPen(color)); - painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); - painter->restore(); -} - -void QtPropertyEditorView::keyPressEvent(QKeyEvent *event) -{ - switch (event->key()) { - case Qt::Key_Return: - case Qt::Key_Enter: - case Qt::Key_Space: // Trigger Edit - if (!m_editorPrivate->editedItem()) - if (const QTreeWidgetItem *item = currentItem()) - if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { - event->accept(); - // If the current position is at column 0, move to 1. - QModelIndex index = currentIndex(); - if (index.column() == 0) { - index = index.sibling(index.row(), 1); - setCurrentIndex(index); - } - edit(index); - return; - } - break; - default: - break; - } - QTreeWidget::keyPressEvent(event); -} - -void QtPropertyEditorView::mousePressEvent(QMouseEvent *event) -{ - QTreeWidget::mousePressEvent(event); - QTreeWidgetItem *item = itemAt(event->pos()); - - if (item) { - if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton) - && (header()->logicalIndexAt(event->pos().x()) == 1) - && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { - editItem(item, 1); - } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) { - if (event->pos().x() + header()->offset() < 20) - item->setExpanded(!item->isExpanded()); - } - } -} - -// ------------ QtPropertyEditorDelegate -class QtPropertyEditorDelegate : public QItemDelegate -{ - Q_OBJECT -public: - explicit QtPropertyEditorDelegate(QObject *parent = 0) - : QItemDelegate(parent), m_editorPrivate(0), m_editedItem(0), m_editedWidget(0), m_disablePainting(false) - {} - - void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) - { m_editorPrivate = editorPrivate; } - - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - - void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; - - void setModelData(QWidget *, QAbstractItemModel *, - const QModelIndex &) const {} - - void setEditorData(QWidget *, const QModelIndex &) const {} - - bool eventFilter(QObject *object, QEvent *event); - void closeEditor(QtProperty *property); - - QTreeWidgetItem *editedItem() const { return m_editedItem; } - -protected: - - void drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QPixmap &pixmap) const; - void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QString &text) const; - -private slots: - void slotEditorDestroyed(QObject *object); - -private: - int indentation(const QModelIndex &index) const; - - typedef QMap EditorToPropertyMap; - mutable EditorToPropertyMap m_editorToProperty; - - typedef QMap PropertyToEditorMap; - mutable PropertyToEditorMap m_propertyToEditor; - QtTreePropertyBrowserPrivate *m_editorPrivate; - mutable QTreeWidgetItem *m_editedItem; - mutable QWidget *m_editedWidget; - mutable bool m_disablePainting; -}; - -int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const -{ - if (!m_editorPrivate) - return 0; - - QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); - int indent = 0; - while (item->parent()) { - item = item->parent(); - ++indent; - } - if (m_editorPrivate->treeWidget()->rootIsDecorated()) - ++indent; - return indent * m_editorPrivate->treeWidget()->indentation(); -} - -void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object) -{ - if (QWidget *w = qobject_cast(object)) { - const EditorToPropertyMap::iterator it = m_editorToProperty.find(w); - if (it != m_editorToProperty.end()) { - m_propertyToEditor.remove(it.value()); - m_editorToProperty.erase(it); - } - if (m_editedWidget == w) { - m_editedWidget = 0; - m_editedItem = 0; - } - } -} - -void QtPropertyEditorDelegate::closeEditor(QtProperty *property) -{ - if (QWidget *w = m_propertyToEditor.value(property, 0)) - w->deleteLater(); -} - -QWidget *QtPropertyEditorDelegate::createEditor(QWidget *parent, - const QStyleOptionViewItem &, const QModelIndex &index) const -{ - if (index.column() == 1 && m_editorPrivate) { - QtProperty *property = m_editorPrivate->indexToProperty(index); - QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); - if (property && item && (item->flags() & Qt::ItemIsEnabled)) { - QWidget *editor = m_editorPrivate->createEditor(property, parent); - if (editor) { - editor->setAutoFillBackground(true); - editor->installEventFilter(const_cast(this)); - connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); - m_propertyToEditor[property] = editor; - m_editorToProperty[editor] = property; - m_editedItem = item; - m_editedWidget = editor; - } - return editor; - } - } - return 0; -} - -void QtPropertyEditorDelegate::updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - Q_UNUSED(index) - editor->setGeometry(option.rect.adjusted(0, 0, 0, -1)); -} - -void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - bool hasValue = true; - if (m_editorPrivate) { - QtProperty *property = m_editorPrivate->indexToProperty(index); - if (property) - hasValue = property->hasValue(); - } - QStyleOptionViewItem opt = option; - if ((m_editorPrivate && index.column() == 0) || !hasValue) { - QtProperty *property = m_editorPrivate->indexToProperty(index); - if (property && property->isModified()) { - opt.font.setBold(true); - opt.fontMetrics = QFontMetrics(opt.font); - } - } - QColor c; - if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { - c = opt.palette.color(QPalette::Dark); - opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText)); - } else { - c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); - if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate)) - c = c.lighter(112); - } - if (c.isValid()) - painter->fillRect(option.rect, c); - opt.state &= ~QStyle::State_HasFocus; - if (index.column() == 1) { - QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); - if (m_editedItem && m_editedItem == item) - m_disablePainting = true; - } - QItemDelegate::paint(painter, opt, index); - if (option.type) - m_disablePainting = false; - - opt.palette.setCurrentColorGroup(QPalette::Active); - QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); - painter->save(); - painter->setPen(QPen(color)); - if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) { - int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left(); - painter->drawLine(right, option.rect.y(), right, option.rect.bottom()); - } - painter->restore(); -} - -void QtPropertyEditorDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QPixmap &pixmap) const -{ - if (m_disablePainting) - return; - - QItemDelegate::drawDecoration(painter, option, rect, pixmap); -} - -void QtPropertyEditorDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QString &text) const -{ - if (m_disablePainting) - return; - - QItemDelegate::drawDisplay(painter, option, rect, text); -} - -QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - return QItemDelegate::sizeHint(option, index) + dpiScaled(QSize(3, 4)); -} - -bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event) -{ - if (event->type() == QEvent::FocusOut) { - QFocusEvent *fe = static_cast(event); - if (fe->reason() == Qt::ActiveWindowFocusReason) - return false; - } - return QItemDelegate::eventFilter(object, event); -} - -// -------- QtTreePropertyBrowserPrivate implementation -QtTreePropertyBrowserPrivate::QtTreePropertyBrowserPrivate() : - m_treeWidget(0), - m_headerVisible(true), - m_allowMultiSelection(false), - m_resizeMode(QtTreePropertyBrowser::Stretch), - m_delegate(0), - m_markPropertiesWithoutValue(false), - m_browserChangedBlocked(false) -{ -} - -// Draw an icon indicating opened/closing branches -static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style) -{ - QPixmap pix(14, 14); - pix.fill(Qt::transparent); - QStyleOption branchOption; - QRect r(QPoint(0, 0), pix.size()); - branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp - branchOption.palette = palette; - branchOption.state = QStyle::State_Children; - - QPainter p; - // Draw closed state - p.begin(&pix); - style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); - p.end(); - QIcon rc = pix; - rc.addPixmap(pix, QIcon::Selected, QIcon::Off); - // Draw opened state - branchOption.state |= QStyle::State_Open; - pix.fill(Qt::transparent); - p.begin(&pix); - style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); - p.end(); - - rc.addPixmap(pix, QIcon::Normal, QIcon::On); - rc.addPixmap(pix, QIcon::Selected, QIcon::On); - return rc; -} - -void QtTreePropertyBrowserPrivate::init(QWidget *parent) -{ - QHBoxLayout *layout = new QHBoxLayout(parent); - layout->setContentsMargins(0, 0, 0, 0); - m_treeWidget = new QtPropertyEditorView(parent); - m_treeWidget->setEditorPrivate(this); - m_treeWidget->setIconSize(QSize(18, 18)); - layout->addWidget(m_treeWidget); - parent->setFocusProxy(m_treeWidget); - - m_treeWidget->setColumnCount(2); - QStringList labels; - labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Property")); - labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Value")); - m_treeWidget->setHeaderLabels(labels); - m_treeWidget->setAlternatingRowColors(true); - m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed); - m_delegate = new QtPropertyEditorDelegate(parent); - m_delegate->setEditorPrivate(this); - m_treeWidget->setItemDelegate(m_delegate); -#if QT_VERSION >= 0x050000 - m_treeWidget->header()->setSectionsMovable(false); - m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); -#else - m_treeWidget->header()->setMovable(false); - m_treeWidget->header()->setResizeMode(QHeaderView::Stretch); -#endif - - m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style()); - - QObject::connect(m_treeWidget, SIGNAL(collapsed(const QModelIndex &)), q_ptr, SLOT(slotCollapsed(const QModelIndex &))); - QObject::connect(m_treeWidget, SIGNAL(expanded(const QModelIndex &)), q_ptr, SLOT(slotExpanded(const QModelIndex &))); - QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); - QObject::connect(m_treeWidget, SIGNAL(itemSelectionChanged()), q_ptr, SLOT(slotItemSelectionChanged())); -} - -QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const -{ - if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem()) - return m_itemToIndex.value(treeItem); - return 0; -} - -void QtTreePropertyBrowserPrivate::setCurrentItem(QtBrowserItem *browserItem, bool block) -{ - const bool blocked = block ? m_treeWidget->blockSignals(true) : false; - if (browserItem == 0) - m_treeWidget->setCurrentItem(0); - else - m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem)); - if (block) - m_treeWidget->blockSignals(blocked); -} - -QtProperty *QtTreePropertyBrowserPrivate::indexToProperty(const QModelIndex &index) const -{ - QTreeWidgetItem *item = m_treeWidget->indexToItem(index); - QtBrowserItem *idx = m_itemToIndex.value(item); - if (idx) - return idx->property(); - return 0; -} - -QtBrowserItem *QtTreePropertyBrowserPrivate::indexToBrowserItem(const QModelIndex &index) const -{ - QTreeWidgetItem *item = m_treeWidget->indexToItem(index); - return m_itemToIndex.value(item); -} - -QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const -{ - return m_treeWidget->indexToItem(index); -} - -bool QtTreePropertyBrowserPrivate::lastColumn(int column) const -{ - return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1; -} - -void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const -{ - Qt::ItemFlags flags = item->flags(); - if (flags & Qt::ItemIsEnabled) { - flags &= ~Qt::ItemIsEnabled; - item->setFlags(flags); - m_delegate->closeEditor(m_itemToIndex[item]->property()); - const int childCount = item->childCount(); - for (int i = 0; i < childCount; i++) { - QTreeWidgetItem *child = item->child(i); - disableItem(child); - } - } -} - -void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const -{ - Qt::ItemFlags flags = item->flags(); - flags |= Qt::ItemIsEnabled; - item->setFlags(flags); - const int childCount = item->childCount(); - for (int i = 0; i < childCount; i++) { - QTreeWidgetItem *child = item->child(i); - QtProperty *property = m_itemToIndex[child]->property(); - if (property->isEnabled()) { - enableItem(child); - } - } -} - -bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const -{ - QtBrowserItem *browserItem = m_itemToIndex.value(item); - if (browserItem) - return browserItem->property()->hasValue(); - return false; -} - -void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) -{ - QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex); - QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent()); - - QTreeWidgetItem *newItem = 0; - if (parentItem) { - newItem = new QTreeWidgetItem(parentItem, afterItem); - } else { - newItem = new QTreeWidgetItem(m_treeWidget, afterItem); - } - m_itemToIndex[newItem] = index; - m_indexToItem[index] = newItem; - - newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); - newItem->setExpanded(true); - - updateItem(newItem); -} - -void QtTreePropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) -{ - QTreeWidgetItem *item = m_indexToItem.value(index); - - if (m_treeWidget->currentItem() == item) { - m_treeWidget->setCurrentItem(0); - } - - delete item; - - m_indexToItem.remove(index); - m_itemToIndex.remove(item); - m_indexToBackgroundColor.remove(index); -} - -void QtTreePropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) -{ - QTreeWidgetItem *item = m_indexToItem.value(index); - - updateItem(item); -} - -void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item) -{ - QtProperty *property = m_itemToIndex[item]->property(); - - if (property->nameColor().isValid()) - item->setForeground(0, QBrush(property->nameColor())); - if (property->valueColor().isValid()) - item->setForeground(1, QBrush(property->valueColor())); - - QIcon expandIcon; - if (property->hasValue()) { - QString toolTip = property->toolTip(); - if (toolTip.isEmpty()) - toolTip = property->displayText(); - item->setToolTip(1, toolTip); - item->setIcon(1, property->valueIcon()); - property->displayText().isEmpty() ? item->setText(1, property->valueText()) : item->setText(1, property->displayText()); - } else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated()) { - expandIcon = m_expandIcon; - } - item->setIcon(0, expandIcon); - item->setFirstColumnSpanned(!property->hasValue()); - item->setToolTip(0, property->propertyName()); - item->setStatusTip(0, property->statusTip()); - item->setWhatsThis(0, property->whatsThis()); - item->setText(0, property->propertyName()); - bool wasEnabled = item->flags() & Qt::ItemIsEnabled; - bool isEnabled = wasEnabled; - if (property->isEnabled()) { - QTreeWidgetItem *parent = item->parent(); - if (!parent || (parent->flags() & Qt::ItemIsEnabled)) - isEnabled = true; - else - isEnabled = false; - } else { - isEnabled = false; - } - if (wasEnabled != isEnabled) { - if (isEnabled) - enableItem(item); - else - disableItem(item); - } - m_treeWidget->viewport()->update(); -} - -QColor QtTreePropertyBrowserPrivate::calculatedBackgroundColor(QtBrowserItem *item) const -{ - QtBrowserItem *i = item; - const QMap::const_iterator itEnd = m_indexToBackgroundColor.constEnd(); - while (i) { - QMap::const_iterator it = m_indexToBackgroundColor.constFind(i); - if (it != itEnd) - return it.value(); - i = i->parent(); - } - return QColor(); -} - -void QtTreePropertyBrowserPrivate::slotCollapsed(const QModelIndex &index) -{ - QTreeWidgetItem *item = indexToItem(index); - QtBrowserItem *idx = m_itemToIndex.value(item); - if (item) - emit q_ptr->collapsed(idx); -} - -void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index) -{ - QTreeWidgetItem *item = indexToItem(index); - QtBrowserItem *idx = m_itemToIndex.value(item); - if (item) - emit q_ptr->expanded(idx); -} - -void QtTreePropertyBrowserPrivate::slotCurrentBrowserItemChanged(QtBrowserItem *item) -{ - if (!m_browserChangedBlocked && item != currentItem()) - setCurrentItem(item, true); -} - -void QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *) -{ - QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0; - m_browserChangedBlocked = true; - q_ptr->setCurrentItem(browserItem); - m_browserChangedBlocked = false; -} - -void QtTreePropertyBrowserPrivate::slotItemSelectionChanged() -{ - emit q_ptr->selectedItemsChanged(); -} - -QTreeWidgetItem *QtTreePropertyBrowserPrivate::editedItem() const -{ - return m_delegate->editedItem(); -} - -QtBrowserItem *QtTreePropertyBrowserPrivate::editedBrowserItem() const -{ - return m_itemToIndex.value(editedItem()); -} - -void QtTreePropertyBrowserPrivate::editItem(QtBrowserItem *browserItem) -{ - if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, 0)) { - m_treeWidget->setCurrentItem (treeItem, 1); - m_treeWidget->editItem(treeItem, 1); - } -} - -QList QtTreePropertyBrowserPrivate::selectedItems() const -{ - const QList selectedTreeItems = m_treeWidget->selectedItems(); - QList browserItems; - - for (QTreeWidgetItem *treeItem : selectedTreeItems) - if (QtBrowserItem *browserItem = m_itemToIndex.value(treeItem, 0)) - browserItems.append(browserItem); - - return browserItems; -} - -/*! - \class QtTreePropertyBrowser - - \brief The QtTreePropertyBrowser class provides QTreeWidget based - property browser. - - A property browser is a widget that enables the user to edit a - given set of properties. Each property is represented by a label - specifying the property's name, and an editing widget (e.g. a line - edit or a combobox) holding its value. A property can have zero or - more subproperties. - - QtTreePropertyBrowser provides a tree based view for all nested - properties, i.e. properties that have subproperties can be in an - expanded (subproperties are visible) or collapsed (subproperties - are hidden) state. For example: - - \image qttreepropertybrowser.png - - Use the QtAbstractPropertyBrowser API to add, insert and remove - properties from an instance of the QtTreePropertyBrowser class. - The properties themselves are created and managed by - implementations of the QtAbstractPropertyManager class. - - \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser -*/ - -/*! - \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item) - - This signal is emitted when the \a item is collapsed. - - \sa expanded(), setExpanded() -*/ - -/*! - \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item) - - This signal is emitted when the \a item is expanded. - - \sa collapsed(), setExpanded() -*/ - -/*! - Creates a property browser with the given \a parent. -*/ -QtTreePropertyBrowser::QtTreePropertyBrowser(QWidget *parent) - : QtAbstractPropertyBrowser(parent) -{ - d_ptr = new QtTreePropertyBrowserPrivate; - d_ptr->q_ptr = this; - - d_ptr->init(this); - connect(this, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentBrowserItemChanged(QtBrowserItem*))); -} - -/*! - Destroys this property browser. - - Note that the properties that were inserted into this browser are - \e not destroyed since they may still be used in other - browsers. The properties are owned by the manager that created - them. - - \sa QtProperty, QtAbstractPropertyManager -*/ -QtTreePropertyBrowser::~QtTreePropertyBrowser() -{ - delete d_ptr; -} - -/*! - \property QtTreePropertyBrowser::indentation - \brief indentation of the items in the tree view. -*/ -int QtTreePropertyBrowser::indentation() const -{ - return d_ptr->m_treeWidget->indentation(); -} - -void QtTreePropertyBrowser::setIndentation(int i) -{ - d_ptr->m_treeWidget->setIndentation(i); -} - -/*! - \property QtTreePropertyBrowser::rootIsDecorated - \brief whether to show controls for expanding and collapsing root items. -*/ -bool QtTreePropertyBrowser::rootIsDecorated() const -{ - return d_ptr->m_treeWidget->rootIsDecorated(); -} - -void QtTreePropertyBrowser::setRootIsDecorated(bool show) -{ - d_ptr->m_treeWidget->setRootIsDecorated(show); - QMapIterator it(d_ptr->m_itemToIndex); - while (it.hasNext()) { - QtProperty *property = it.next().value()->property(); - if (!property->hasValue()) - d_ptr->updateItem(it.key()); - } -} - -/*! - \property QtTreePropertyBrowser::alternatingRowColors - \brief whether to draw the background using alternating colors. - By default this property is set to true. -*/ -bool QtTreePropertyBrowser::alternatingRowColors() const -{ - return d_ptr->m_treeWidget->alternatingRowColors(); -} - -void QtTreePropertyBrowser::setAlternatingRowColors(bool enable) -{ - d_ptr->m_treeWidget->setAlternatingRowColors(enable); - QMapIterator it(d_ptr->m_itemToIndex); -} - -/*! - \property QtTreePropertyBrowser::headerVisible - \brief whether to show the header. -*/ -bool QtTreePropertyBrowser::isHeaderVisible() const -{ - return d_ptr->m_headerVisible; -} - -void QtTreePropertyBrowser::setHeaderVisible(bool visible) -{ - if (d_ptr->m_headerVisible == visible) - return; - - d_ptr->m_headerVisible = visible; - d_ptr->m_treeWidget->header()->setVisible(visible); -} - -/*! - \property QtTreePropertyBrowser::allowMultiSelection - \brief whether to allow multiple selection items. -*/ -bool QtTreePropertyBrowser::allowMultiSelection() const -{ - return d_ptr->m_allowMultiSelection; -} - -void QtTreePropertyBrowser::setAllowMultiSelection(bool multiSelection) -{ - if (d_ptr->m_allowMultiSelection == multiSelection) - return; - - d_ptr->m_allowMultiSelection = multiSelection; - d_ptr->m_treeWidget->setSelectionMode(multiSelection ? QAbstractItemView::ExtendedSelection - : QAbstractItemView::SingleSelection); -} - -/*! - \enum QtTreePropertyBrowser::ResizeMode - - The resize mode specifies the behavior of the header sections. - - \value Interactive The user can resize the sections. - The sections can also be resized programmatically using setSplitterPosition(). - - \value Fixed The user cannot resize the section. - The section can only be resized programmatically using setSplitterPosition(). - - \value Stretch QHeaderView will automatically resize the section to fill the available space. - The size cannot be changed by the user or programmatically. - - \value ResizeToContents QHeaderView will automatically resize the section to its optimal - size based on the contents of the entire column. - The size cannot be changed by the user or programmatically. - - \sa setResizeMode() -*/ - -/*! - \property QtTreePropertyBrowser::resizeMode - \brief the resize mode of setions in the header. -*/ - -QtTreePropertyBrowser::ResizeMode QtTreePropertyBrowser::resizeMode() const -{ - return d_ptr->m_resizeMode; -} - -void QtTreePropertyBrowser::setResizeMode(QtTreePropertyBrowser::ResizeMode mode) -{ - if (d_ptr->m_resizeMode == mode) - return; - - d_ptr->m_resizeMode = mode; - QHeaderView::ResizeMode m = QHeaderView::Stretch; - switch (mode) { - case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive; break; - case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed; break; - case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents; break; - case QtTreePropertyBrowser::Stretch: - default: m = QHeaderView::Stretch; break; - } -#if QT_VERSION >= 0x050000 - d_ptr->m_treeWidget->header()->setSectionResizeMode(m); -#else - d_ptr->m_treeWidget->header()->setResizeMode(m); -#endif -} - -/*! - \property QtTreePropertyBrowser::splitterPosition - \brief the position of the splitter between the colunms. -*/ - -int QtTreePropertyBrowser::splitterPosition() const -{ - return d_ptr->m_treeWidget->header()->sectionSize(0); -} - -void QtTreePropertyBrowser::setSplitterPosition(int position) -{ - d_ptr->m_treeWidget->header()->resizeSection(0, position); -} - -int QtTreePropertyBrowser::scrollPosition() const -{ - return d_ptr->m_treeWidget->verticalScrollBar()->value(); -} - -void QtTreePropertyBrowser::setScrollPosition(int value) -{ - d_ptr->m_treeWidget->verticalScrollBar()->setValue(value); -} - -/*! - Sets the \a item to either collapse or expanded, depending on the value of \a expanded. - - \sa isExpanded(), expanded(), collapsed() -*/ - -void QtTreePropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) -{ - QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); - if (treeItem) - treeItem->setExpanded(expanded); -} - -/*! - Returns true if the \a item is expanded; otherwise returns false. - - \sa setExpanded() -*/ - -bool QtTreePropertyBrowser::isExpanded(QtBrowserItem *item) const -{ - QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); - if (treeItem) - return treeItem->isExpanded(); - return false; -} - -/*! - Returns true if the \a item is visible; otherwise returns false. - - \sa setItemVisible() - \since 4.5 -*/ - -bool QtTreePropertyBrowser::isItemVisible(QtBrowserItem *item) const -{ - if (const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) - return !treeItem->isHidden(); - return false; -} - -/*! - Sets the \a item to be visible, depending on the value of \a visible. - - \sa isItemVisible() - \since 4.5 -*/ - -void QtTreePropertyBrowser::setItemVisible(QtBrowserItem *item, bool visible) -{ - if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) - treeItem->setHidden(!visible); -} - -/*! - Sets the \a item's background color to \a color. Note that while item's background - is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color) - - \sa backgroundColor(), calculatedBackgroundColor() -*/ - -void QtTreePropertyBrowser::setBackgroundColor(QtBrowserItem *item, const QColor &color) -{ - if (!d_ptr->m_indexToItem.contains(item)) - return; - if (color.isValid()) - d_ptr->m_indexToBackgroundColor[item] = color; - else - d_ptr->m_indexToBackgroundColor.remove(item); - d_ptr->m_treeWidget->viewport()->update(); -} - -/*! - Returns the \a item's color. If there is no color set for item it returns invalid color. - - \sa calculatedBackgroundColor(), setBackgroundColor() -*/ - -QColor QtTreePropertyBrowser::backgroundColor(QtBrowserItem *item) const -{ - return d_ptr->m_indexToBackgroundColor.value(item); -} - -/*! - Returns the \a item's color. If there is no color set for item it returns parent \a item's - color (if there is no color set for parent it returns grandparent's color and so on). In case - the color is not set for \a item and it's top level item it returns invalid color. - - \sa backgroundColor(), setBackgroundColor() -*/ - -QColor QtTreePropertyBrowser::calculatedBackgroundColor(QtBrowserItem *item) const -{ - return d_ptr->calculatedBackgroundColor(item); -} - -/*! - \property QtTreePropertyBrowser::propertiesWithoutValueMarked - \brief whether to enable or disable marking properties without value. - - When marking is enabled the item's background is rendered in dark color and item's - foreground is rendered with light color. - - \sa propertiesWithoutValueMarked() -*/ -void QtTreePropertyBrowser::setPropertiesWithoutValueMarked(bool mark) -{ - if (d_ptr->m_markPropertiesWithoutValue == mark) - return; - - d_ptr->m_markPropertiesWithoutValue = mark; - QMapIterator it(d_ptr->m_itemToIndex); - while (it.hasNext()) { - QtProperty *property = it.next().value()->property(); - if (!property->hasValue()) - d_ptr->updateItem(it.key()); - } - d_ptr->m_treeWidget->viewport()->update(); -} - -bool QtTreePropertyBrowser::propertiesWithoutValueMarked() const -{ - return d_ptr->m_markPropertiesWithoutValue; -} - -/*! - \reimp -*/ -void QtTreePropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) -{ - d_ptr->propertyInserted(item, afterItem); -} - -/*! - \reimp -*/ -void QtTreePropertyBrowser::itemRemoved(QtBrowserItem *item) -{ - d_ptr->propertyRemoved(item); -} - -/*! - \reimp -*/ -void QtTreePropertyBrowser::itemChanged(QtBrowserItem *item) -{ - d_ptr->propertyChanged(item); -} - -/*! - Sets the current item to \a item and opens the relevant editor for it. -*/ -void QtTreePropertyBrowser::editItem(QtBrowserItem *item) -{ - d_ptr->editItem(item); -} - -/*! - Returns the current item being edited, if any. -*/ -QtBrowserItem *QtTreePropertyBrowser::editedItem() const -{ - return d_ptr->editedBrowserItem(); -} - -/*! - Returns the selected items. - - \sa setAllowMultiSelection() -*/ -QList QtTreePropertyBrowser::selectedItems() const -{ - return d_ptr->selectedItems(); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qttreepropertybrowser.cpp" -#include "qttreepropertybrowser.moc" diff --git a/src/qtpropertybrowser/src/qttreepropertybrowser.h b/src/qtpropertybrowser/src/qttreepropertybrowser.h deleted file mode 100644 index 6364a36ec9..0000000000 --- a/src/qtpropertybrowser/src/qttreepropertybrowser.h +++ /dev/null @@ -1,150 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTTREEPROPERTYBROWSER_H -#define QTTREEPROPERTYBROWSER_H - -#include "qtpropertybrowser.h" - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QTreeWidgetItem; -class QtTreePropertyBrowserPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtTreePropertyBrowser : public QtAbstractPropertyBrowser -{ - Q_OBJECT - Q_ENUMS(ResizeMode) - Q_PROPERTY(int indentation READ indentation WRITE setIndentation) - Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated) - Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors) - Q_PROPERTY(bool headerVisible READ isHeaderVisible WRITE setHeaderVisible) - Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) - Q_PROPERTY(int splitterPosition READ splitterPosition WRITE setSplitterPosition) - Q_PROPERTY(bool propertiesWithoutValueMarked READ propertiesWithoutValueMarked WRITE setPropertiesWithoutValueMarked) -public: - - enum ResizeMode - { - Interactive, - Stretch, - Fixed, - ResizeToContents - }; - - QtTreePropertyBrowser(QWidget *parent = 0); - ~QtTreePropertyBrowser(); - - int indentation() const; - void setIndentation(int i); - - bool rootIsDecorated() const; - void setRootIsDecorated(bool show); - - bool alternatingRowColors() const; - void setAlternatingRowColors(bool enable); - - bool isHeaderVisible() const; - void setHeaderVisible(bool visible); - - bool allowMultiSelection() const; - void setAllowMultiSelection(bool multiSelection); - - ResizeMode resizeMode() const; - void setResizeMode(ResizeMode mode); - - int splitterPosition() const; - void setSplitterPosition(int position); - - int scrollPosition() const; - void setScrollPosition(int value); - - void setExpanded(QtBrowserItem *item, bool expanded); - bool isExpanded(QtBrowserItem *item) const; - - bool isItemVisible(QtBrowserItem *item) const; - void setItemVisible(QtBrowserItem *item, bool visible); - - void setBackgroundColor(QtBrowserItem *item, const QColor &color); - QColor backgroundColor(QtBrowserItem *item) const; - QColor calculatedBackgroundColor(QtBrowserItem *item) const; - - void setPropertiesWithoutValueMarked(bool mark); - bool propertiesWithoutValueMarked() const; - - void editItem(QtBrowserItem *item); - QtBrowserItem *editedItem() const; - - QList selectedItems() const; - -Q_SIGNALS: - - void collapsed(QtBrowserItem *item); - void expanded(QtBrowserItem *item); - - void selectedItemsChanged(); - -protected: - virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); - virtual void itemRemoved(QtBrowserItem *item); - virtual void itemChanged(QtBrowserItem *item); - -private: - - QtTreePropertyBrowserPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtTreePropertyBrowser) - Q_DISABLE_COPY(QtTreePropertyBrowser) - - Q_PRIVATE_SLOT(d_func(), void slotCollapsed(const QModelIndex &)) - Q_PRIVATE_SLOT(d_func(), void slotExpanded(const QModelIndex &)) - Q_PRIVATE_SLOT(d_func(), void slotCurrentBrowserItemChanged(QtBrowserItem *)) - Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)) - Q_PRIVATE_SLOT(d_func(), void slotItemSelectionChanged()) - -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#endif diff --git a/src/qtpropertybrowser/src/qtvariantproperty.cpp b/src/qtpropertybrowser/src/qtvariantproperty.cpp deleted file mode 100644 index b2b4c1de3c..0000000000 --- a/src/qtpropertybrowser/src/qtvariantproperty.cpp +++ /dev/null @@ -1,2291 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtvariantproperty.h" -#include "qtpropertymanager.h" -#include "qteditorfactory.h" -#include -#include -#include -#include - -#if defined(Q_CC_MSVC) -# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ -#endif - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -class QtEnumPropertyType -{ -}; - - -class QtFlagPropertyType -{ -}; - - -class QtGroupPropertyType -{ -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -Q_DECLARE_METATYPE(QtEnumPropertyType) -Q_DECLARE_METATYPE(QtFlagPropertyType) -Q_DECLARE_METATYPE(QtGroupPropertyType) - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -/*! - Returns the type id for an enum property. - - Note that the property's value type can be retrieved using the - valueType() function (which is QVariant::Int for the enum property - type). - - \sa propertyType(), valueType() -*/ -int QtVariantPropertyManager::enumTypeId() -{ - return qMetaTypeId(); -} - -/*! - Returns the type id for a flag property. - - Note that the property's value type can be retrieved using the - valueType() function (which is QVariant::Int for the flag property - type). - - \sa propertyType(), valueType() -*/ -int QtVariantPropertyManager::flagTypeId() -{ - return qMetaTypeId(); -} - -/*! - Returns the type id for a group property. - - Note that the property's value type can be retrieved using the - valueType() function (which is QVariant::Invalid for the group - property type, since it doesn't provide any value). - - \sa propertyType(), valueType() -*/ -int QtVariantPropertyManager::groupTypeId() -{ - return qMetaTypeId(); -} - -/*! - Returns the type id for a icon map attribute. - - Note that the property's attribute type can be retrieved using the - attributeType() function. - - \sa attributeType(), QtEnumPropertyManager::enumIcons() -*/ -int QtVariantPropertyManager::iconMapTypeId() -{ - return qMetaTypeId(); -} - -typedef QMap PropertyMap; -Q_GLOBAL_STATIC(PropertyMap, propertyToWrappedProperty) - -static QtProperty *wrappedProperty(QtProperty *property) -{ - return propertyToWrappedProperty()->value(property, 0); -} - -class QtVariantPropertyPrivate -{ -public: - QtVariantPropertyPrivate(QtVariantPropertyManager *m) : manager(m) {} - - QtVariantPropertyManager *manager; -}; - -/*! - \class QtVariantProperty - - \brief The QtVariantProperty class is a convenience class handling - QVariant based properties. - - QtVariantProperty provides additional API: A property's type, - value type, attribute values and current value can easily be - retrieved using the propertyType(), valueType(), attributeValue() - and value() functions respectively. In addition, the attribute - values and the current value can be set using the corresponding - setValue() and setAttribute() functions. - - For example, instead of writing: - - \code - QtVariantPropertyManager *variantPropertyManager; - QtProperty *property; - - variantPropertyManager->setValue(property, 10); - \endcode - - you can write: - - \code - QtVariantPropertyManager *variantPropertyManager; - QtVariantProperty *property; - - property->setValue(10); - \endcode - - QtVariantProperty instances can only be created by the - QtVariantPropertyManager class. - - \sa QtProperty, QtVariantPropertyManager, QtVariantEditorFactory -*/ - -/*! - Creates a variant property using the given \a manager. - - Do not use this constructor to create variant property instances; - use the QtVariantPropertyManager::addProperty() function - instead. This constructor is used internally by the - QtVariantPropertyManager::createProperty() function. - - \sa QtVariantPropertyManager -*/ -QtVariantProperty::QtVariantProperty(QtVariantPropertyManager *manager) - : QtProperty(manager), d_ptr(new QtVariantPropertyPrivate(manager)) -{ - -} - -/*! - Destroys this property. - - \sa QtProperty::~QtProperty() -*/ -QtVariantProperty::~QtVariantProperty() -{ - delete d_ptr; -} - -/*! - Returns the property's current value. - - \sa valueType(), setValue() -*/ -QVariant QtVariantProperty::value() const -{ - return d_ptr->manager->value(this); -} - -/*! - Returns this property's value for the specified \a attribute. - - QtVariantPropertyManager provides a couple of related functions: - \l{QtVariantPropertyManager::attributes()}{attributes()} and - \l{QtVariantPropertyManager::attributeType()}{attributeType()}. - - \sa setAttribute() -*/ -QVariant QtVariantProperty::attributeValue(const QString &attribute) const -{ - return d_ptr->manager->attributeValue(this, attribute); -} - -/*! - Returns the type of this property's value. - - \sa propertyType() -*/ -int QtVariantProperty::valueType() const -{ - return d_ptr->manager->valueType(this); -} - -/*! - Returns this property's type. - - QtVariantPropertyManager provides several related functions: - \l{QtVariantPropertyManager::enumTypeId()}{enumTypeId()}, - \l{QtVariantPropertyManager::flagTypeId()}{flagTypeId()} and - \l{QtVariantPropertyManager::groupTypeId()}{groupTypeId()}. - - \sa valueType() -*/ -int QtVariantProperty::propertyType() const -{ - return d_ptr->manager->propertyType(this); -} - -/*! - Sets the value of this property to \a value. - - The specified \a value must be of the type returned by - valueType(), or of a type that can be converted to valueType() - using the QVariant::canConvert() function; otherwise this function - does nothing. - - \sa value() -*/ -void QtVariantProperty::setValue(const QVariant &value) -{ - d_ptr->manager->setValue(this, value); -} - -/*! - Sets the \a attribute of property to \a value. - - QtVariantPropertyManager provides the related - \l{QtVariantPropertyManager::setAttribute()}{setAttribute()} - function. - - \sa attributeValue() -*/ -void QtVariantProperty::setAttribute(const QString &attribute, const QVariant &value) -{ - d_ptr->manager->setAttribute(this, attribute, value); -} - -class QtVariantPropertyManagerPrivate -{ - QtVariantPropertyManager *q_ptr; - Q_DECLARE_PUBLIC(QtVariantPropertyManager) -public: - QtVariantPropertyManagerPrivate(); - - bool m_creatingProperty; - bool m_creatingSubProperties; - bool m_destroyingSubProperties; - int m_propertyType; - - void slotValueChanged(QtProperty *property, int val); - void slotRangeChanged(QtProperty *property, int min, int max); - void slotSingleStepChanged(QtProperty *property, int step); - void slotValueChanged(QtProperty *property, double val); - void slotRangeChanged(QtProperty *property, double min, double max); - void slotSingleStepChanged(QtProperty *property, double step); - void slotDecimalsChanged(QtProperty *property, int prec); - void slotValueChanged(QtProperty *property, bool val); - void slotValueChanged(QtProperty *property, const QString &val); - void slotRegExpChanged(QtProperty *property, const QRegularExpression ®Exp); - void slotEchoModeChanged(QtProperty *property, int); - void slotValueChanged(QtProperty *property, const QDate &val); - void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); - void slotValueChanged(QtProperty *property, const QTime &val); - void slotValueChanged(QtProperty *property, const QDateTime &val); - void slotValueChanged(QtProperty *property, const QKeySequence &val); - void slotValueChanged(QtProperty *property, const QChar &val); - void slotValueChanged(QtProperty *property, const QLocale &val); - void slotValueChanged(QtProperty *property, const QPoint &val); - void slotValueChanged(QtProperty *property, const QPointF &val); - void slotValueChanged(QtProperty *property, const QSize &val); - void slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max); - void slotValueChanged(QtProperty *property, const QSizeF &val); - void slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max); - void slotValueChanged(QtProperty *property, const QRect &val); - void slotConstraintChanged(QtProperty *property, const QRect &val); - void slotValueChanged(QtProperty *property, const QRectF &val); - void slotConstraintChanged(QtProperty *property, const QRectF &val); - void slotValueChanged(QtProperty *property, const QColor &val); - void slotEnumChanged(QtProperty *property, int val); - void slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames); - void slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons); - void slotValueChanged(QtProperty *property, const QSizePolicy &val); - void slotValueChanged(QtProperty *property, const QFont &val); - void slotValueChanged(QtProperty *property, const QCursor &val); - void slotFlagChanged(QtProperty *property, int val); - void slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames); - void slotReadOnlyChanged(QtProperty *property, bool readOnly); - void slotTextVisibleChanged(QtProperty *property, bool textVisible); - void slotPropertyInserted(QtProperty *property, QtProperty *parent, QtProperty *after); - void slotPropertyRemoved(QtProperty *property, QtProperty *parent); - - void valueChanged(QtProperty *property, const QVariant &val); - - int internalPropertyToType(QtProperty *property) const; - QtVariantProperty *createSubProperty(QtVariantProperty *parent, QtVariantProperty *after, - QtProperty *internal); - void removeSubProperty(QtVariantProperty *property); - - QMap m_typeToPropertyManager; - QMap > m_typeToAttributeToAttributeType; - - QMap > m_propertyToType; - - QMap m_typeToValueType; - - - QMap m_internalToProperty; - - const QString m_constraintAttribute; - const QString m_singleStepAttribute; - const QString m_decimalsAttribute; - const QString m_enumIconsAttribute; - const QString m_enumNamesAttribute; - const QString m_flagNamesAttribute; - const QString m_maximumAttribute; - const QString m_minimumAttribute; - const QString m_regExpAttribute; - const QString m_echoModeAttribute; - const QString m_readOnlyAttribute; - const QString m_textVisibleAttribute; -}; - -QtVariantPropertyManagerPrivate::QtVariantPropertyManagerPrivate() : - m_constraintAttribute(QLatin1String("constraint")), - m_singleStepAttribute(QLatin1String("singleStep")), - m_decimalsAttribute(QLatin1String("decimals")), - m_enumIconsAttribute(QLatin1String("enumIcons")), - m_enumNamesAttribute(QLatin1String("enumNames")), - m_flagNamesAttribute(QLatin1String("flagNames")), - m_maximumAttribute(QLatin1String("maximum")), - m_minimumAttribute(QLatin1String("minimum")), - m_regExpAttribute(QLatin1String("regExp")), - m_echoModeAttribute(QLatin1String("echoMode")), - m_readOnlyAttribute(QLatin1String("readOnly")), - m_textVisibleAttribute(QLatin1String("textVisible")) -{ -} - -int QtVariantPropertyManagerPrivate::internalPropertyToType(QtProperty *property) const -{ - int type = 0; - QtAbstractPropertyManager *internPropertyManager = property->propertyManager(); - if (qobject_cast(internPropertyManager)) - type = QVariant::Int; - else if (qobject_cast(internPropertyManager)) - type = QtVariantPropertyManager::enumTypeId(); - else if (qobject_cast(internPropertyManager)) - type = QVariant::Bool; - else if (qobject_cast(internPropertyManager)) - type = QVariant::Double; - return type; -} - -QtVariantProperty *QtVariantPropertyManagerPrivate::createSubProperty(QtVariantProperty *parent, - QtVariantProperty *after, QtProperty *internal) -{ - int type = internalPropertyToType(internal); - if (!type) - return 0; - - bool wasCreatingSubProperties = m_creatingSubProperties; - m_creatingSubProperties = true; - - QtVariantProperty *varChild = q_ptr->addProperty(type, internal->propertyName()); - - m_creatingSubProperties = wasCreatingSubProperties; - - varChild->setPropertyName(internal->propertyName()); - varChild->setToolTip(internal->toolTip()); - varChild->setStatusTip(internal->statusTip()); - varChild->setWhatsThis(internal->whatsThis()); - - m_internalToProperty[internal] = varChild; - propertyToWrappedProperty()->insert(varChild, internal); - - parent->insertSubProperty(varChild, after); - - return varChild; -} - -void QtVariantPropertyManagerPrivate::removeSubProperty(QtVariantProperty *property) -{ - QtProperty *internChild = wrappedProperty(property); - bool wasDestroyingSubProperties = m_destroyingSubProperties; - m_destroyingSubProperties = true; - delete property; - m_destroyingSubProperties = wasDestroyingSubProperties; - m_internalToProperty.remove(internChild); - propertyToWrappedProperty()->remove(property); -} - -void QtVariantPropertyManagerPrivate::slotPropertyInserted(QtProperty *property, - QtProperty *parent, QtProperty *after) -{ - if (m_creatingProperty) - return; - - QtVariantProperty *varParent = m_internalToProperty.value(parent, 0); - if (!varParent) - return; - - QtVariantProperty *varAfter = 0; - if (after) { - varAfter = m_internalToProperty.value(after, 0); - if (!varAfter) - return; - } - - createSubProperty(varParent, varAfter, property); -} - -void QtVariantPropertyManagerPrivate::slotPropertyRemoved(QtProperty *property, QtProperty *parent) -{ - Q_UNUSED(parent) - - QtVariantProperty *varProperty = m_internalToProperty.value(property, 0); - if (!varProperty) - return; - - removeSubProperty(varProperty); -} - -void QtVariantPropertyManagerPrivate::valueChanged(QtProperty *property, const QVariant &val) -{ - QtVariantProperty *varProp = m_internalToProperty.value(property, 0); - if (!varProp) - return; - emit q_ptr->valueChanged(varProp, val); - emit q_ptr->propertyChanged(varProp); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, int val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, int min, int max) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { - emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); - emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); - } -} - -void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, int step) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, double val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, double min, double max) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { - emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); - emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); - } -} - -void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, double step) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); -} - -void QtVariantPropertyManagerPrivate::slotDecimalsChanged(QtProperty *property, int prec) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_decimalsAttribute, QVariant(prec)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, bool val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QString &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRegExpChanged(QtProperty *property, const QRegularExpression ®Exp) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_regExpAttribute, QVariant(regExp)); -} - -void QtVariantPropertyManagerPrivate::slotEchoModeChanged(QtProperty *property, int mode) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_echoModeAttribute, QVariant(mode)); -} - -void QtVariantPropertyManagerPrivate::slotReadOnlyChanged(QtProperty *property, bool readOnly) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_readOnlyAttribute, QVariant(readOnly)); -} - -void QtVariantPropertyManagerPrivate::slotTextVisibleChanged(QtProperty *property, bool textVisible) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_textVisibleAttribute, QVariant(textVisible)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDate &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { - emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); - emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); - } -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QTime &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDateTime &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QKeySequence &val) -{ - valueChanged(property, QVariant::fromValue(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QChar &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QLocale &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPoint &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPointF &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSize &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { - emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); - emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); - } -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizeF &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { - emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); - emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); - } -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRect &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRect &constraint) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRectF &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRectF &constraint) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QColor &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_enumNamesAttribute, QVariant(enumNames)); -} - -void QtVariantPropertyManagerPrivate::slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_enumIconsAttribute, QVariant::fromValue(enumIcons)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizePolicy &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QFont &val) -{ - valueChanged(property, QVariant(val)); -} - -void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QCursor &val) -{ -#ifndef QT_NO_CURSOR - valueChanged(property, QVariant(val)); -#endif -} - -void QtVariantPropertyManagerPrivate::slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames) -{ - if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) - emit q_ptr->attributeChanged(varProp, m_flagNamesAttribute, QVariant(flagNames)); -} - -/*! - \class QtVariantPropertyManager - - \brief The QtVariantPropertyManager class provides and manages QVariant based properties. - - QtVariantPropertyManager provides the addProperty() function which - creates QtVariantProperty objects. The QtVariantProperty class is - a convenience class handling QVariant based properties inheriting - QtProperty. A QtProperty object created by a - QtVariantPropertyManager instance can be converted into a - QtVariantProperty object using the variantProperty() function. - - The property's value can be retrieved using the value(), and set - using the setValue() slot. In addition the property's type, and - the type of its value, can be retrieved using the propertyType() - and valueType() functions respectively. - - A property's type is a QVariant::Type enumerator value, and - usually a property's type is the same as its value type. But for - some properties the types differ, for example for enums, flags and - group types in which case QtVariantPropertyManager provides the - enumTypeId(), flagTypeId() and groupTypeId() functions, - respectively, to identify their property type (the value types are - QVariant::Int for the enum and flag types, and QVariant::Invalid - for the group type). - - Use the isPropertyTypeSupported() function to check if a particular - property type is supported. The currently supported property types - are: - - \table - \header - \o Property Type - \o Property Type Id - \row - \o int - \o QVariant::Int - \row - \o double - \o QVariant::Double - \row - \o bool - \o QVariant::Bool - \row - \o QString - \o QVariant::String - \row - \o QDate - \o QVariant::Date - \row - \o QTime - \o QVariant::Time - \row - \o QDateTime - \o QVariant::DateTime - \row - \o QKeySequence - \o QVariant::KeySequence - \row - \o QChar - \o QVariant::Char - \row - \o QLocale - \o QVariant::Locale - \row - \o QPoint - \o QVariant::Point - \row - \o QPointF - \o QVariant::PointF - \row - \o QSize - \o QVariant::Size - \row - \o QSizeF - \o QVariant::SizeF - \row - \o QRect - \o QVariant::Rect - \row - \o QRectF - \o QVariant::RectF - \row - \o QColor - \o QVariant::Color - \row - \o QSizePolicy - \o QVariant::SizePolicy - \row - \o QFont - \o QVariant::Font - \row - \o QCursor - \o QVariant::Cursor - \row - \o enum - \o enumTypeId() - \row - \o flag - \o flagTypeId() - \row - \o group - \o groupTypeId() - \endtable - - Each property type can provide additional attributes, - e.g. QVariant::Int and QVariant::Double provides minimum and - maximum values. The currently supported attributes are: - - \table - \header - \o Property Type - \o Attribute Name - \o Attribute Type - \row - \o \c int - \o minimum - \o QVariant::Int - \row - \o - \o maximum - \o QVariant::Int - \row - \o - \o singleStep - \o QVariant::Int - \row - \o \c double - \o minimum - \o QVariant::Double - \row - \o - \o maximum - \o QVariant::Double - \row - \o - \o singleStep - \o QVariant::Double - \row - \o bool - \o textVisible - \o QVariant::Bool - \row - \o - \o decimals - \o QVariant::Int - \row - \o QString - \o regExp - \o QVariant::RegExp - \row - \o - \o echoMode - \o QVariant::Int - \row - \o QDate - \o minimum - \o QVariant::Date - \row - \o - \o maximum - \o QVariant::Date - \row - \o QPointF - \o decimals - \o QVariant::Int - \row - \o QSize - \o minimum - \o QVariant::Size - \row - \o - \o maximum - \o QVariant::Size - \row - \o QSizeF - \o minimum - \o QVariant::SizeF - \row - \o - \o maximum - \o QVariant::SizeF - \row - \o - \o decimals - \o QVariant::Int - \row - \o QRect - \o constraint - \o QVariant::Rect - \row - \o QRectF - \o constraint - \o QVariant::RectF - \row - \o - \o decimals - \o QVariant::Int - \row - \o \c enum - \o enumNames - \o QVariant::StringList - \row - \o - \o enumIcons - \o iconMapTypeId() - \row - \o \c flag - \o flagNames - \o QVariant::StringList - \endtable - - The attributes for a given property type can be retrieved using - the attributes() function. Each attribute has a value type which - can be retrieved using the attributeType() function, and a value - accessible through the attributeValue() function. In addition, the - value can be set using the setAttribute() slot. - - QtVariantManager also provides the valueChanged() signal which is - emitted whenever a property created by this manager change, and - the attributeChanged() signal which is emitted whenever an - attribute of such a property changes. - - \sa QtVariantProperty, QtVariantEditorFactory -*/ - -/*! - \fn void QtVariantPropertyManager::valueChanged(QtProperty *property, const QVariant &value) - - This signal is emitted whenever a property created by this manager - changes its value, passing a pointer to the \a property and the - new \a value as parameters. - - \sa setValue() -*/ - -/*! - \fn void QtVariantPropertyManager::attributeChanged(QtProperty *property, - const QString &attribute, const QVariant &value) - - This signal is emitted whenever an attribute of a property created - by this manager changes its value, passing a pointer to the \a - property, the \a attribute and the new \a value as parameters. - - \sa setAttribute() -*/ - -/*! - Creates a manager with the given \a parent. -*/ -QtVariantPropertyManager::QtVariantPropertyManager(QObject *parent) - : QtAbstractPropertyManager(parent) -{ - d_ptr = new QtVariantPropertyManagerPrivate; - d_ptr->q_ptr = this; - - d_ptr->m_creatingProperty = false; - d_ptr->m_creatingSubProperties = false; - d_ptr->m_destroyingSubProperties = false; - d_ptr->m_propertyType = 0; - - // IntPropertyManager - QtIntPropertyManager *intPropertyManager = new QtIntPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Int] = intPropertyManager; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_minimumAttribute] = QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_maximumAttribute] = QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_singleStepAttribute] = QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_readOnlyAttribute] = QVariant::Bool; - d_ptr->m_typeToValueType[QVariant::Int] = QVariant::Int; - connect(intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(intPropertyManager, SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(intPropertyManager, SIGNAL(singleStepChanged(QtProperty *, int)), - this, SLOT(slotSingleStepChanged(QtProperty *, int))); - // DoublePropertyManager - QtDoublePropertyManager *doublePropertyManager = new QtDoublePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Double] = doublePropertyManager; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_minimumAttribute] = - QVariant::Double; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_maximumAttribute] = - QVariant::Double; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_singleStepAttribute] = - QVariant::Double; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_decimalsAttribute] = - QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_readOnlyAttribute] = - QVariant::Bool; - d_ptr->m_typeToValueType[QVariant::Double] = QVariant::Double; - connect(doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotValueChanged(QtProperty *, double))); - connect(doublePropertyManager, SIGNAL(rangeChanged(QtProperty *, double, double)), - this, SLOT(slotRangeChanged(QtProperty *, double, double))); - connect(doublePropertyManager, SIGNAL(singleStepChanged(QtProperty *, double)), - this, SLOT(slotSingleStepChanged(QtProperty *, double))); - connect(doublePropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - // BoolPropertyManager - QtBoolPropertyManager *boolPropertyManager = new QtBoolPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Bool] = boolPropertyManager; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Bool][d_ptr->m_textVisibleAttribute] = QVariant::Bool; - d_ptr->m_typeToValueType[QVariant::Bool] = QVariant::Bool; - connect(boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotValueChanged(QtProperty *, bool))); - connect(boolPropertyManager, SIGNAL(textVisibleChanged(QtProperty*, bool)), - this, SLOT(slotTextVisibleChanged(QtProperty*, bool))); - // StringPropertyManager - QtStringPropertyManager *stringPropertyManager = new QtStringPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::String] = stringPropertyManager; - d_ptr->m_typeToValueType[QVariant::String] = QVariant::String; - d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_regExpAttribute] = - QVariant::RegularExpression; - d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_echoModeAttribute] = - QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_readOnlyAttribute] = - QVariant::Bool; - - connect(stringPropertyManager, SIGNAL(valueChanged(QtProperty *, const QString &)), - this, SLOT(slotValueChanged(QtProperty *, const QString &))); - connect(stringPropertyManager, SIGNAL(regExpChanged(QtProperty *, const QRegularExpression &)), - this, SLOT(slotRegExpChanged(QtProperty *, const QRegularExpression &))); - connect(stringPropertyManager, SIGNAL(echoModeChanged(QtProperty*,int)), - this, SLOT(slotEchoModeChanged(QtProperty*, int))); - connect(stringPropertyManager, SIGNAL(readOnlyChanged(QtProperty*, bool)), - this, SLOT(slotReadOnlyChanged(QtProperty*, bool))); - - // DatePropertyManager - QtDatePropertyManager *datePropertyManager = new QtDatePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Date] = datePropertyManager; - d_ptr->m_typeToValueType[QVariant::Date] = QVariant::Date; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_minimumAttribute] = - QVariant::Date; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_maximumAttribute] = - QVariant::Date; - connect(datePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDate &)), - this, SLOT(slotValueChanged(QtProperty *, const QDate &))); - connect(datePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), - this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); - // TimePropertyManager - QtTimePropertyManager *timePropertyManager = new QtTimePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Time] = timePropertyManager; - d_ptr->m_typeToValueType[QVariant::Time] = QVariant::Time; - connect(timePropertyManager, SIGNAL(valueChanged(QtProperty *, const QTime &)), - this, SLOT(slotValueChanged(QtProperty *, const QTime &))); - // DateTimePropertyManager - QtDateTimePropertyManager *dateTimePropertyManager = new QtDateTimePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::DateTime] = dateTimePropertyManager; - d_ptr->m_typeToValueType[QVariant::DateTime] = QVariant::DateTime; - connect(dateTimePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), - this, SLOT(slotValueChanged(QtProperty *, const QDateTime &))); - // KeySequencePropertyManager - QtKeySequencePropertyManager *keySequencePropertyManager = new QtKeySequencePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::KeySequence] = keySequencePropertyManager; - d_ptr->m_typeToValueType[QVariant::KeySequence] = QVariant::KeySequence; - connect(keySequencePropertyManager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), - this, SLOT(slotValueChanged(QtProperty *, const QKeySequence &))); - // CharPropertyManager - QtCharPropertyManager *charPropertyManager = new QtCharPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Char] = charPropertyManager; - d_ptr->m_typeToValueType[QVariant::Char] = QVariant::Char; - connect(charPropertyManager, SIGNAL(valueChanged(QtProperty *, const QChar &)), - this, SLOT(slotValueChanged(QtProperty *, const QChar &))); - // PointPropertyManager - QtPointPropertyManager *pointPropertyManager = new QtPointPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Point] = pointPropertyManager; - d_ptr->m_typeToValueType[QVariant::Point] = QVariant::Point; - connect(pointPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPoint &)), - this, SLOT(slotValueChanged(QtProperty *, const QPoint &))); - connect(pointPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(pointPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(pointPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // PointFPropertyManager - QtPointFPropertyManager *pointFPropertyManager = new QtPointFPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::PointF] = pointFPropertyManager; - d_ptr->m_typeToValueType[QVariant::PointF] = QVariant::PointF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::PointF][d_ptr->m_decimalsAttribute] = - QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[QVariant::PointF][d_ptr->m_singleStepAttribute] = - QVariant::Double; - connect(pointFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPointF &)), - this, SLOT(slotValueChanged(QtProperty *, const QPointF &))); - connect(pointFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - connect(pointFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotValueChanged(QtProperty *, double))); - connect(pointFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(pointFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // SizePropertyManager - QtSizePropertyManager *sizePropertyManager = new QtSizePropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Size] = sizePropertyManager; - d_ptr->m_typeToValueType[QVariant::Size] = QVariant::Size; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_minimumAttribute] = - QVariant::Size; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_maximumAttribute] = - QVariant::Size; - connect(sizePropertyManager, SIGNAL(valueChanged(QtProperty *, const QSize &)), - this, SLOT(slotValueChanged(QtProperty *, const QSize &))); - connect(sizePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSize &, const QSize &)), - this, SLOT(slotRangeChanged(QtProperty *, const QSize &, const QSize &))); - connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(sizePropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(sizePropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // SizeFPropertyManager - QtSizeFPropertyManager *sizeFPropertyManager = new QtSizeFPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::SizeF] = sizeFPropertyManager; - d_ptr->m_typeToValueType[QVariant::SizeF] = QVariant::SizeF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_minimumAttribute] = - QVariant::SizeF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_maximumAttribute] = - QVariant::SizeF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_decimalsAttribute] = - QVariant::Int; - connect(sizeFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QSizeF &)), - this, SLOT(slotValueChanged(QtProperty *, const QSizeF &))); - connect(sizeFPropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSizeF &, const QSizeF &)), - this, SLOT(slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &))); - connect(sizeFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotValueChanged(QtProperty *, double))); - connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), - this, SLOT(slotRangeChanged(QtProperty *, double, double))); - connect(sizeFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(sizeFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // RectPropertyManager - QtRectPropertyManager *rectPropertyManager = new QtRectPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Rect] = rectPropertyManager; - d_ptr->m_typeToValueType[QVariant::Rect] = QVariant::Rect; - d_ptr->m_typeToAttributeToAttributeType[QVariant::Rect][d_ptr->m_constraintAttribute] = - QVariant::Rect; - connect(rectPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRect &)), - this, SLOT(slotValueChanged(QtProperty *, const QRect &))); - connect(rectPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRect &)), - this, SLOT(slotConstraintChanged(QtProperty *, const QRect &))); - connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(rectPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(rectPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // RectFPropertyManager - QtRectFPropertyManager *rectFPropertyManager = new QtRectFPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::RectF] = rectFPropertyManager; - d_ptr->m_typeToValueType[QVariant::RectF] = QVariant::RectF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_constraintAttribute] = - QVariant::RectF; - d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_decimalsAttribute] = - QVariant::Int; - connect(rectFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRectF &)), - this, SLOT(slotValueChanged(QtProperty *, const QRectF &))); - connect(rectFPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRectF &)), - this, SLOT(slotConstraintChanged(QtProperty *, const QRectF &))); - connect(rectFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), - this, SLOT(slotDecimalsChanged(QtProperty *, int))); - connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), - this, SLOT(slotValueChanged(QtProperty *, double))); - connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), - this, SLOT(slotRangeChanged(QtProperty *, double, double))); - connect(rectFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(rectFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // ColorPropertyManager - QtColorPropertyManager *colorPropertyManager = new QtColorPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Color] = colorPropertyManager; - d_ptr->m_typeToValueType[QVariant::Color] = QVariant::Color; - connect(colorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QColor &)), - this, SLOT(slotValueChanged(QtProperty *, const QColor &))); - connect(colorPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(colorPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(colorPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // EnumPropertyManager - int enumId = enumTypeId(); - QtEnumPropertyManager *enumPropertyManager = new QtEnumPropertyManager(this); - d_ptr->m_typeToPropertyManager[enumId] = enumPropertyManager; - d_ptr->m_typeToValueType[enumId] = QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumNamesAttribute] = - QVariant::StringList; - d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumIconsAttribute] = - iconMapTypeId(); - connect(enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(enumPropertyManager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), - this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); - connect(enumPropertyManager, SIGNAL(enumIconsChanged(QtProperty *, const QMap &)), - this, SLOT(slotEnumIconsChanged(QtProperty *, const QMap &))); - // FontPropertyManager - QtFontPropertyManager *fontPropertyManager = new QtFontPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Font] = fontPropertyManager; - d_ptr->m_typeToValueType[QVariant::Font] = QVariant::Font; - connect(fontPropertyManager, SIGNAL(valueChanged(QtProperty *, const QFont &)), - this, SLOT(slotValueChanged(QtProperty *, const QFont &))); - connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), - this, SLOT(slotRangeChanged(QtProperty *, int, int))); - connect(fontPropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(fontPropertyManager->subEnumPropertyManager(), - SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), - this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); - connect(fontPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotValueChanged(QtProperty *, bool))); - connect(fontPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(fontPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // CursorPropertyManager - QtCursorPropertyManager *cursorPropertyManager = new QtCursorPropertyManager(this); - d_ptr->m_typeToPropertyManager[QVariant::Cursor] = cursorPropertyManager; - d_ptr->m_typeToValueType[QVariant::Cursor] = QVariant::Cursor; - connect(cursorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), - this, SLOT(slotValueChanged(QtProperty *, const QCursor &))); - // FlagPropertyManager - int flagId = flagTypeId(); - QtFlagPropertyManager *flagPropertyManager = new QtFlagPropertyManager(this); - d_ptr->m_typeToPropertyManager[flagId] = flagPropertyManager; - d_ptr->m_typeToValueType[flagId] = QVariant::Int; - d_ptr->m_typeToAttributeToAttributeType[flagId][d_ptr->m_flagNamesAttribute] = - QVariant::StringList; - connect(flagPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), - this, SLOT(slotValueChanged(QtProperty *, int))); - connect(flagPropertyManager, SIGNAL(flagNamesChanged(QtProperty *, const QStringList &)), - this, SLOT(slotFlagNamesChanged(QtProperty *, const QStringList &))); - connect(flagPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), - this, SLOT(slotValueChanged(QtProperty *, bool))); - connect(flagPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), - this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); - connect(flagPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), - this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); - // FlagPropertyManager - int groupId = groupTypeId(); - QtGroupPropertyManager *groupPropertyManager = new QtGroupPropertyManager(this); - d_ptr->m_typeToPropertyManager[groupId] = groupPropertyManager; - d_ptr->m_typeToValueType[groupId] = QVariant::Invalid; -} - -/*! - Destroys this manager, and all the properties it has created. -*/ -QtVariantPropertyManager::~QtVariantPropertyManager() -{ - clear(); - delete d_ptr; -} - -/*! - Returns the given \a property converted into a QtVariantProperty. - - If the \a property was not created by this variant manager, the - function returns 0. - - \sa createProperty() -*/ -QtVariantProperty *QtVariantPropertyManager::variantProperty(const QtProperty *property) const -{ - const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); - if (it == d_ptr->m_propertyToType.constEnd()) - return 0; - return it.value().first; -} - -/*! - Returns true if the given \a propertyType is supported by this - variant manager; otherwise false. - - \sa propertyType() -*/ -bool QtVariantPropertyManager::isPropertyTypeSupported(int propertyType) const -{ - if (d_ptr->m_typeToValueType.contains(propertyType)) - return true; - return false; -} - -/*! - Creates and returns a variant property of the given \a propertyType - with the given \a name. - - If the specified \a propertyType is not supported by this variant - manager, this function returns 0. - - Do not use the inherited - QtAbstractPropertyManager::addProperty() function to create a - variant property (that function will always return 0 since it will - not be clear what type the property should have). - - \sa isPropertyTypeSupported() -*/ -QtVariantProperty *QtVariantPropertyManager::addProperty(int propertyType, const QString &name) -{ - if (!isPropertyTypeSupported(propertyType)) - return 0; - - bool wasCreating = d_ptr->m_creatingProperty; - d_ptr->m_creatingProperty = true; - d_ptr->m_propertyType = propertyType; - QtProperty *property = QtAbstractPropertyManager::addProperty(name); - d_ptr->m_creatingProperty = wasCreating; - d_ptr->m_propertyType = 0; - - if (!property) - return 0; - - return variantProperty(property); -} - -/*! - Returns the given \a property's value. - - If the given \a property is not managed by this manager, this - function returns an invalid variant. - - \sa setValue() -*/ -QVariant QtVariantPropertyManager::value(const QtProperty *property) const -{ - QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - if (internProp == 0) - return QVariant(); - - QtAbstractPropertyManager *manager = internProp->propertyManager(); - if (QtIntPropertyManager *intManager = qobject_cast(manager)) { - return intManager->value(internProp); - } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { - return doubleManager->value(internProp); - } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { - return boolManager->value(internProp); - } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { - return stringManager->value(internProp); - } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { - return dateManager->value(internProp); - } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { - return timeManager->value(internProp); - } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { - return dateTimeManager->value(internProp); - } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { -#if QT_VERSION < 0x050000 - return keySequenceManager->value(internProp); -#else - return QVariant::fromValue(keySequenceManager->value(internProp)); -#endif - } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { - return charManager->value(internProp); - } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { - return pointManager->value(internProp); - } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { - return pointFManager->value(internProp); - } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { - return sizeManager->value(internProp); - } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { - return sizeFManager->value(internProp); - } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { - return rectManager->value(internProp); - } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { - return rectFManager->value(internProp); - } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { - return colorManager->value(internProp); - } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { - return enumManager->value(internProp); - } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { - return fontManager->value(internProp); -#ifndef QT_NO_CURSOR - } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { - return cursorManager->value(internProp); -#endif - } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { - return flagManager->value(internProp); - } - return QVariant(); -} - -/*! - Returns the given \a property's value type. - - \sa propertyType() -*/ -int QtVariantPropertyManager::valueType(const QtProperty *property) const -{ - int propType = propertyType(property); - return valueType(propType); -} - -/*! - \overload - - Returns the value type associated with the given \a propertyType. -*/ -int QtVariantPropertyManager::valueType(int propertyType) const -{ - if (d_ptr->m_typeToValueType.contains(propertyType)) - return d_ptr->m_typeToValueType[propertyType]; - return 0; -} - -/*! - Returns the given \a property's type. - - \sa valueType() -*/ -int QtVariantPropertyManager::propertyType(const QtProperty *property) const -{ - const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); - if (it == d_ptr->m_propertyToType.constEnd()) - return 0; - return it.value().second; -} - -/*! - Returns the given \a property's value for the specified \a - attribute - - If the given \a property was not created by \e this manager, or if - the specified \a attribute does not exist, this function returns - an invalid variant. - - \sa attributes(), attributeType(), setAttribute() -*/ -QVariant QtVariantPropertyManager::attributeValue(const QtProperty *property, const QString &attribute) const -{ - int propType = propertyType(property); - if (!propType) - return QVariant(); - - QMap >::ConstIterator it = - d_ptr->m_typeToAttributeToAttributeType.find(propType); - if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) - return QVariant(); - - QMap attributes = it.value(); - QMap::ConstIterator itAttr = attributes.find(attribute); - if (itAttr == attributes.constEnd()) - return QVariant(); - - QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - if (internProp == 0) - return QVariant(); - - QtAbstractPropertyManager *manager = internProp->propertyManager(); - if (QtIntPropertyManager *intManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - return intManager->maximum(internProp); - if (attribute == d_ptr->m_minimumAttribute) - return intManager->minimum(internProp); - if (attribute == d_ptr->m_singleStepAttribute) - return intManager->singleStep(internProp); - if (attribute == d_ptr->m_readOnlyAttribute) - return intManager->isReadOnly(internProp); - return QVariant(); - } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - return doubleManager->maximum(internProp); - if (attribute == d_ptr->m_minimumAttribute) - return doubleManager->minimum(internProp); - if (attribute == d_ptr->m_singleStepAttribute) - return doubleManager->singleStep(internProp); - if (attribute == d_ptr->m_decimalsAttribute) - return doubleManager->decimals(internProp); - if (attribute == d_ptr->m_readOnlyAttribute) - return doubleManager->isReadOnly(internProp); - return QVariant(); - } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_textVisibleAttribute) - return boolManager->textVisible(internProp); - return QVariant(); - } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_regExpAttribute) - return stringManager->regExp(internProp); - if (attribute == d_ptr->m_echoModeAttribute) - return stringManager->echoMode(internProp); - if (attribute == d_ptr->m_readOnlyAttribute) - return stringManager->isReadOnly(internProp); - return QVariant(); - } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - return dateManager->maximum(internProp); - if (attribute == d_ptr->m_minimumAttribute) - return dateManager->minimum(internProp); - return QVariant(); - } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_singleStepAttribute) - return pointFManager->singleStep(internProp); - if (attribute == d_ptr->m_decimalsAttribute) - return pointFManager->decimals(internProp); - return QVariant(); - } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - return sizeManager->maximum(internProp); - if (attribute == d_ptr->m_minimumAttribute) - return sizeManager->minimum(internProp); - return QVariant(); - } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - return sizeFManager->maximum(internProp); - if (attribute == d_ptr->m_minimumAttribute) - return sizeFManager->minimum(internProp); - if (attribute == d_ptr->m_decimalsAttribute) - return sizeFManager->decimals(internProp); - return QVariant(); - } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_constraintAttribute) - return rectManager->constraint(internProp); - return QVariant(); - } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_constraintAttribute) - return rectFManager->constraint(internProp); - if (attribute == d_ptr->m_decimalsAttribute) - return rectFManager->decimals(internProp); - return QVariant(); - } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_enumNamesAttribute) - return enumManager->enumNames(internProp); - if (attribute == d_ptr->m_enumIconsAttribute) - return QVariant::fromValue(enumManager->enumIcons(internProp)); - return QVariant(); - } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_flagNamesAttribute) - return flagManager->flagNames(internProp); - return QVariant(); - } - return QVariant(); -} - -/*! - Returns a list of the given \a propertyType 's attributes. - - \sa attributeValue(), attributeType() -*/ -QStringList QtVariantPropertyManager::attributes(int propertyType) const -{ - QMap >::ConstIterator it = - d_ptr->m_typeToAttributeToAttributeType.find(propertyType); - if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) - return QStringList(); - return it.value().keys(); -} - -/*! - Returns the type of the specified \a attribute of the given \a - propertyType. - - If the given \a propertyType is not supported by \e this manager, - or if the given \a propertyType does not possess the specified \a - attribute, this function returns QVariant::Invalid. - - \sa attributes(), valueType() -*/ -int QtVariantPropertyManager::attributeType(int propertyType, const QString &attribute) const -{ - QMap >::ConstIterator it = - d_ptr->m_typeToAttributeToAttributeType.find(propertyType); - if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) - return 0; - - QMap attributes = it.value(); - QMap::ConstIterator itAttr = attributes.find(attribute); - if (itAttr == attributes.constEnd()) - return 0; - return itAttr.value(); -} - -/*! - \fn void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &value) - - Sets the value of the given \a property to \a value. - - The specified \a value must be of a type returned by valueType(), - or of type that can be converted to valueType() using the - QVariant::canConvert() function, otherwise this function does - nothing. - - \sa value(), QtVariantProperty::setValue(), valueChanged() -*/ -void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &val) -{ - int propType = val.userType(); - if (!propType) - return; - - int valType = valueType(property); - - if (propType != valType && !val.canConvert(static_cast(valType))) - return; - - QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - if (internProp == 0) - return; - - - QtAbstractPropertyManager *manager = internProp->propertyManager(); - if (QtIntPropertyManager *intManager = qobject_cast(manager)) { - intManager->setValue(internProp, val.value()); - return; - } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { - doubleManager->setValue(internProp, val.value()); - return; - } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { - boolManager->setValue(internProp, val.value()); - return; - } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { - stringManager->setValue(internProp, val.value()); - return; - } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { - dateManager->setValue(internProp, val.value()); - return; - } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { - timeManager->setValue(internProp, val.value()); - return; - } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { - dateTimeManager->setValue(internProp, val.value()); - return; - } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { - keySequenceManager->setValue(internProp, val.value()); - return; - } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { - charManager->setValue(internProp, val.value()); - return; - } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { - pointManager->setValue(internProp, val.value()); - return; - } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { - pointFManager->setValue(internProp, val.value()); - return; - } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { - sizeManager->setValue(internProp, val.value()); - return; - } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { - sizeFManager->setValue(internProp, val.value()); - return; - } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { - rectManager->setValue(internProp, val.value()); - return; - } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { - rectFManager->setValue(internProp, val.value()); - return; - } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { - colorManager->setValue(internProp, val.value()); - return; - } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { - enumManager->setValue(internProp, val.value()); - return; - } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { - fontManager->setValue(internProp, val.value()); - return; -#ifndef QT_NO_CURSOR - } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { - cursorManager->setValue(internProp, val.value()); - return; -#endif - } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { - flagManager->setValue(internProp, val.value()); - return; - } -} - -/*! - Sets the value of the specified \a attribute of the given \a - property, to \a value. - - The new \a value's type must be of the type returned by - attributeType(), or of a type that can be converted to - attributeType() using the QVariant::canConvert() function, - otherwise this function does nothing. - - \sa attributeValue(), QtVariantProperty::setAttribute(), attributeChanged() -*/ -void QtVariantPropertyManager::setAttribute(QtProperty *property, - const QString &attribute, const QVariant &value) -{ - QVariant oldAttr = attributeValue(property, attribute); - if (!oldAttr.isValid()) - return; - - int attrType = value.userType(); - if (!attrType) - return; - - if (attrType != attributeType(propertyType(property), attribute) && - !value.canConvert((QVariant::Type)attrType)) - return; - - QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - if (internProp == 0) - return; - - QtAbstractPropertyManager *manager = internProp->propertyManager(); - if (QtIntPropertyManager *intManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - intManager->setMaximum(internProp, value.value()); - else if (attribute == d_ptr->m_minimumAttribute) - intManager->setMinimum(internProp, value.value()); - else if (attribute == d_ptr->m_singleStepAttribute) - intManager->setSingleStep(internProp, value.value()); - else if (attribute == d_ptr->m_readOnlyAttribute) - intManager->setReadOnly(internProp, value.value()); - return; - } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - doubleManager->setMaximum(internProp, value.value()); - if (attribute == d_ptr->m_minimumAttribute) - doubleManager->setMinimum(internProp, value.value()); - if (attribute == d_ptr->m_singleStepAttribute) - doubleManager->setSingleStep(internProp, value.value()); - if (attribute == d_ptr->m_decimalsAttribute) - doubleManager->setDecimals(internProp, value.value()); - if (attribute == d_ptr->m_readOnlyAttribute) - doubleManager->setReadOnly(internProp, value.value()); - return; - } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_textVisibleAttribute) - boolManager->setTextVisible(internProp, value.value()); - return; - } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_regExpAttribute) - stringManager->setRegExp(internProp, value.value()); - if (attribute == d_ptr->m_echoModeAttribute) - stringManager->setEchoMode(internProp, (EchoMode)value.value()); - if (attribute == d_ptr->m_readOnlyAttribute) - stringManager->setReadOnly(internProp, (EchoMode)value.value()); - return; - } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - dateManager->setMaximum(internProp, value.value()); - if (attribute == d_ptr->m_minimumAttribute) - dateManager->setMinimum(internProp, value.value()); - return; - } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_singleStepAttribute) - pointFManager->setSingleStep(internProp, value.value()); - if (attribute == d_ptr->m_decimalsAttribute) - pointFManager->setDecimals(internProp, value.value()); - return; - } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - sizeManager->setMaximum(internProp, value.value()); - if (attribute == d_ptr->m_minimumAttribute) - sizeManager->setMinimum(internProp, value.value()); - return; - } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_maximumAttribute) - sizeFManager->setMaximum(internProp, value.value()); - if (attribute == d_ptr->m_minimumAttribute) - sizeFManager->setMinimum(internProp, value.value()); - if (attribute == d_ptr->m_decimalsAttribute) - sizeFManager->setDecimals(internProp, value.value()); - return; - } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_constraintAttribute) - rectManager->setConstraint(internProp, value.value()); - return; - } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_constraintAttribute) - rectFManager->setConstraint(internProp, value.value()); - if (attribute == d_ptr->m_decimalsAttribute) - rectFManager->setDecimals(internProp, value.value()); - return; - } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_enumNamesAttribute) - enumManager->setEnumNames(internProp, value.value()); - if (attribute == d_ptr->m_enumIconsAttribute) - enumManager->setEnumIcons(internProp, value.value()); - return; - } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { - if (attribute == d_ptr->m_flagNamesAttribute) - flagManager->setFlagNames(internProp, value.value()); - return; - } -} - -/*! - \reimp -*/ -bool QtVariantPropertyManager::hasValue(const QtProperty *property) const -{ - if (propertyType(property) == groupTypeId()) - return false; - return true; -} - -/*! - \reimp -*/ -QString QtVariantPropertyManager::valueText(const QtProperty *property) const -{ - const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - return internProp ? !internProp->displayText().isEmpty() ? internProp->displayText() : internProp->valueText() : QString(); -} - -/*! - \reimp -*/ -QIcon QtVariantPropertyManager::valueIcon(const QtProperty *property) const -{ - const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); - return internProp ? internProp->valueIcon() : QIcon(); -} - -/*! - \reimp -*/ -void QtVariantPropertyManager::initializeProperty(QtProperty *property) -{ - QtVariantProperty *varProp = variantProperty(property); - if (!varProp) - return; - - QMap::ConstIterator it = - d_ptr->m_typeToPropertyManager.find(d_ptr->m_propertyType); - if (it != d_ptr->m_typeToPropertyManager.constEnd()) { - QtProperty *internProp = 0; - if (!d_ptr->m_creatingSubProperties) { - QtAbstractPropertyManager *manager = it.value(); - internProp = manager->addProperty(); - d_ptr->m_internalToProperty[internProp] = varProp; - } - propertyToWrappedProperty()->insert(varProp, internProp); - if (internProp) { - QList children = internProp->subProperties(); - QListIterator itChild(children); - QtVariantProperty *lastProperty = 0; - while (itChild.hasNext()) { - QtVariantProperty *prop = d_ptr->createSubProperty(varProp, lastProperty, itChild.next()); - lastProperty = prop ? prop : lastProperty; - } - } - } -} - -/*! - \reimp -*/ -void QtVariantPropertyManager::uninitializeProperty(QtProperty *property) -{ - const QMap >::iterator type_it = d_ptr->m_propertyToType.find(property); - if (type_it == d_ptr->m_propertyToType.end()) - return; - - PropertyMap::iterator it = propertyToWrappedProperty()->find(property); - if (it != propertyToWrappedProperty()->end()) { - QtProperty *internProp = it.value(); - if (internProp) { - d_ptr->m_internalToProperty.remove(internProp); - if (!d_ptr->m_destroyingSubProperties) { - delete internProp; - } - } - propertyToWrappedProperty()->erase(it); - } - d_ptr->m_propertyToType.erase(type_it); -} - -/*! - \reimp -*/ -QtProperty *QtVariantPropertyManager::createProperty() -{ - if (!d_ptr->m_creatingProperty) - return 0; - - QtVariantProperty *property = new QtVariantProperty(this); - d_ptr->m_propertyToType.insert(property, qMakePair(property, d_ptr->m_propertyType)); - - return property; -} - -///////////////////////////// - -class QtVariantEditorFactoryPrivate -{ - QtVariantEditorFactory *q_ptr; - Q_DECLARE_PUBLIC(QtVariantEditorFactory) -public: - - QtSpinBoxFactory *m_spinBoxFactory; - QtDoubleSpinBoxFactory *m_doubleSpinBoxFactory; - QtCheckBoxFactory *m_checkBoxFactory; - QtLineEditFactory *m_lineEditFactory; - QtDateEditFactory *m_dateEditFactory; - QtTimeEditFactory *m_timeEditFactory; - QtDateTimeEditFactory *m_dateTimeEditFactory; - QtKeySequenceEditorFactory *m_keySequenceEditorFactory; - QtCharEditorFactory *m_charEditorFactory; - QtEnumEditorFactory *m_comboBoxFactory; - QtCursorEditorFactory *m_cursorEditorFactory; - QtColorEditorFactory *m_colorEditorFactory; - QtFontEditorFactory *m_fontEditorFactory; - - QMap m_factoryToType; - QMap m_typeToFactory; -}; - -/*! - \class QtVariantEditorFactory - - \brief The QtVariantEditorFactory class provides widgets for properties - created by QtVariantPropertyManager objects. - - The variant factory provides the following widgets for the - specified property types: - - \table - \header - \o Property Type - \o Widget - \row - \o \c int - \o QSpinBox - \row - \o \c double - \o QDoubleSpinBox - \row - \o \c bool - \o QCheckBox - \row - \o QString - \o QLineEdit - \row - \o QDate - \o QDateEdit - \row - \o QTime - \o QTimeEdit - \row - \o QDateTime - \o QDateTimeEdit - \row - \o QKeySequence - \o customized editor - \row - \o QChar - \o customized editor - \row - \o \c enum - \o QComboBox - \row - \o QCursor - \o QComboBox - \endtable - - Note that QtVariantPropertyManager supports several additional property - types for which the QtVariantEditorFactory class does not provide - editing widgets, e.g. QPoint and QSize. To provide widgets for other - types using the variant approach, derive from the QtVariantEditorFactory - class. - - \sa QtAbstractEditorFactory, QtVariantPropertyManager -*/ - -/*! - Creates a factory with the given \a parent. -*/ -QtVariantEditorFactory::QtVariantEditorFactory(QObject *parent) - : QtAbstractEditorFactory(parent) -{ - d_ptr = new QtVariantEditorFactoryPrivate(); - d_ptr->q_ptr = this; - - d_ptr->m_spinBoxFactory = new QtSpinBoxFactory(this); - d_ptr->m_factoryToType[d_ptr->m_spinBoxFactory] = QVariant::Int; - d_ptr->m_typeToFactory[QVariant::Int] = d_ptr->m_spinBoxFactory; - - d_ptr->m_doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); - d_ptr->m_factoryToType[d_ptr->m_doubleSpinBoxFactory] = QVariant::Double; - d_ptr->m_typeToFactory[QVariant::Double] = d_ptr->m_doubleSpinBoxFactory; - - d_ptr->m_checkBoxFactory = new QtCheckBoxFactory(this); - d_ptr->m_factoryToType[d_ptr->m_checkBoxFactory] = QVariant::Bool; - d_ptr->m_typeToFactory[QVariant::Bool] = d_ptr->m_checkBoxFactory; - - d_ptr->m_lineEditFactory = new QtLineEditFactory(this); - d_ptr->m_factoryToType[d_ptr->m_lineEditFactory] = QVariant::String; - d_ptr->m_typeToFactory[QVariant::String] = d_ptr->m_lineEditFactory; - - d_ptr->m_dateEditFactory = new QtDateEditFactory(this); - d_ptr->m_factoryToType[d_ptr->m_dateEditFactory] = QVariant::Date; - d_ptr->m_typeToFactory[QVariant::Date] = d_ptr->m_dateEditFactory; - - d_ptr->m_timeEditFactory = new QtTimeEditFactory(this); - d_ptr->m_factoryToType[d_ptr->m_timeEditFactory] = QVariant::Time; - d_ptr->m_typeToFactory[QVariant::Time] = d_ptr->m_timeEditFactory; - - d_ptr->m_dateTimeEditFactory = new QtDateTimeEditFactory(this); - d_ptr->m_factoryToType[d_ptr->m_dateTimeEditFactory] = QVariant::DateTime; - d_ptr->m_typeToFactory[QVariant::DateTime] = d_ptr->m_dateTimeEditFactory; - - d_ptr->m_keySequenceEditorFactory = new QtKeySequenceEditorFactory(this); - d_ptr->m_factoryToType[d_ptr->m_keySequenceEditorFactory] = QVariant::KeySequence; - d_ptr->m_typeToFactory[QVariant::KeySequence] = d_ptr->m_keySequenceEditorFactory; - - d_ptr->m_charEditorFactory = new QtCharEditorFactory(this); - d_ptr->m_factoryToType[d_ptr->m_charEditorFactory] = QVariant::Char; - d_ptr->m_typeToFactory[QVariant::Char] = d_ptr->m_charEditorFactory; - - d_ptr->m_cursorEditorFactory = new QtCursorEditorFactory(this); - d_ptr->m_factoryToType[d_ptr->m_cursorEditorFactory] = QVariant::Cursor; - d_ptr->m_typeToFactory[QVariant::Cursor] = d_ptr->m_cursorEditorFactory; - - d_ptr->m_colorEditorFactory = new QtColorEditorFactory(this); - d_ptr->m_factoryToType[d_ptr->m_colorEditorFactory] = QVariant::Color; - d_ptr->m_typeToFactory[QVariant::Color] = d_ptr->m_colorEditorFactory; - - d_ptr->m_fontEditorFactory = new QtFontEditorFactory(this); - d_ptr->m_factoryToType[d_ptr->m_fontEditorFactory] = QVariant::Font; - d_ptr->m_typeToFactory[QVariant::Font] = d_ptr->m_fontEditorFactory; - - d_ptr->m_comboBoxFactory = new QtEnumEditorFactory(this); - const int enumId = QtVariantPropertyManager::enumTypeId(); - d_ptr->m_factoryToType[d_ptr->m_comboBoxFactory] = enumId; - d_ptr->m_typeToFactory[enumId] = d_ptr->m_comboBoxFactory; -} - -/*! - Destroys this factory, and all the widgets it has created. -*/ -QtVariantEditorFactory::~QtVariantEditorFactory() -{ - delete d_ptr; -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtVariantEditorFactory::connectPropertyManager(QtVariantPropertyManager *manager) -{ - QList intPropertyManagers = manager->findChildren(); - QListIterator itInt(intPropertyManagers); - while (itInt.hasNext()) - d_ptr->m_spinBoxFactory->addPropertyManager(itInt.next()); - - QList doublePropertyManagers = manager->findChildren(); - QListIterator itDouble(doublePropertyManagers); - while (itDouble.hasNext()) - d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itDouble.next()); - - QList boolPropertyManagers = manager->findChildren(); - QListIterator itBool(boolPropertyManagers); - while (itBool.hasNext()) - d_ptr->m_checkBoxFactory->addPropertyManager(itBool.next()); - - QList stringPropertyManagers = manager->findChildren(); - QListIterator itString(stringPropertyManagers); - while (itString.hasNext()) - d_ptr->m_lineEditFactory->addPropertyManager(itString.next()); - - QList datePropertyManagers = manager->findChildren(); - QListIterator itDate(datePropertyManagers); - while (itDate.hasNext()) - d_ptr->m_dateEditFactory->addPropertyManager(itDate.next()); - - QList timePropertyManagers = manager->findChildren(); - QListIterator itTime(timePropertyManagers); - while (itTime.hasNext()) - d_ptr->m_timeEditFactory->addPropertyManager(itTime.next()); - - QList dateTimePropertyManagers = manager->findChildren(); - QListIterator itDateTime(dateTimePropertyManagers); - while (itDateTime.hasNext()) - d_ptr->m_dateTimeEditFactory->addPropertyManager(itDateTime.next()); - - QList keySequencePropertyManagers = manager->findChildren(); - QListIterator itKeySequence(keySequencePropertyManagers); - while (itKeySequence.hasNext()) - d_ptr->m_keySequenceEditorFactory->addPropertyManager(itKeySequence.next()); - - QList charPropertyManagers = manager->findChildren(); - QListIterator itChar(charPropertyManagers); - while (itChar.hasNext()) - d_ptr->m_charEditorFactory->addPropertyManager(itChar.next()); - - QList pointPropertyManagers = manager->findChildren(); - QListIterator itPoint(pointPropertyManagers); - while (itPoint.hasNext()) - d_ptr->m_spinBoxFactory->addPropertyManager(itPoint.next()->subIntPropertyManager()); - - QList pointFPropertyManagers = manager->findChildren(); - QListIterator itPointF(pointFPropertyManagers); - while (itPointF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itPointF.next()->subDoublePropertyManager()); - - QList sizePropertyManagers = manager->findChildren(); - QListIterator itSize(sizePropertyManagers); - while (itSize.hasNext()) - d_ptr->m_spinBoxFactory->addPropertyManager(itSize.next()->subIntPropertyManager()); - - QList sizeFPropertyManagers = manager->findChildren(); - QListIterator itSizeF(sizeFPropertyManagers); - while (itSizeF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itSizeF.next()->subDoublePropertyManager()); - - QList rectPropertyManagers = manager->findChildren(); - QListIterator itRect(rectPropertyManagers); - while (itRect.hasNext()) - d_ptr->m_spinBoxFactory->addPropertyManager(itRect.next()->subIntPropertyManager()); - - QList rectFPropertyManagers = manager->findChildren(); - QListIterator itRectF(rectFPropertyManagers); - while (itRectF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itRectF.next()->subDoublePropertyManager()); - - QList colorPropertyManagers = manager->findChildren(); - QListIterator itColor(colorPropertyManagers); - while (itColor.hasNext()) { - QtColorPropertyManager *manager = itColor.next(); - d_ptr->m_colorEditorFactory->addPropertyManager(manager); - d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); - } - - QList enumPropertyManagers = manager->findChildren(); - QListIterator itEnum(enumPropertyManagers); - while (itEnum.hasNext()) - d_ptr->m_comboBoxFactory->addPropertyManager(itEnum.next()); - - QList fontPropertyManagers = manager->findChildren(); - QListIterator itFont(fontPropertyManagers); - while (itFont.hasNext()) { - QtFontPropertyManager *manager = itFont.next(); - d_ptr->m_fontEditorFactory->addPropertyManager(manager); - d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); - d_ptr->m_comboBoxFactory->addPropertyManager(manager->subEnumPropertyManager()); - d_ptr->m_checkBoxFactory->addPropertyManager(manager->subBoolPropertyManager()); - } - - QList cursorPropertyManagers = manager->findChildren(); - QListIterator itCursor(cursorPropertyManagers); - while (itCursor.hasNext()) - d_ptr->m_cursorEditorFactory->addPropertyManager(itCursor.next()); - - QList flagPropertyManagers = manager->findChildren(); - QListIterator itFlag(flagPropertyManagers); - while (itFlag.hasNext()) - d_ptr->m_checkBoxFactory->addPropertyManager(itFlag.next()->subBoolPropertyManager()); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -QWidget *QtVariantEditorFactory::createEditor(QtVariantPropertyManager *manager, QtProperty *property, - QWidget *parent) -{ - const int propType = manager->propertyType(property); - QtAbstractEditorFactoryBase *factory = d_ptr->m_typeToFactory.value(propType, 0); - if (!factory) - return 0; - return factory->createEditor(wrappedProperty(property), parent); -} - -/*! - \internal - - Reimplemented from the QtAbstractEditorFactory class. -*/ -void QtVariantEditorFactory::disconnectPropertyManager(QtVariantPropertyManager *manager) -{ - QList intPropertyManagers = manager->findChildren(); - QListIterator itInt(intPropertyManagers); - while (itInt.hasNext()) - d_ptr->m_spinBoxFactory->removePropertyManager(itInt.next()); - - QList doublePropertyManagers = manager->findChildren(); - QListIterator itDouble(doublePropertyManagers); - while (itDouble.hasNext()) - d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itDouble.next()); - - QList boolPropertyManagers = manager->findChildren(); - QListIterator itBool(boolPropertyManagers); - while (itBool.hasNext()) - d_ptr->m_checkBoxFactory->removePropertyManager(itBool.next()); - - QList stringPropertyManagers = manager->findChildren(); - QListIterator itString(stringPropertyManagers); - while (itString.hasNext()) - d_ptr->m_lineEditFactory->removePropertyManager(itString.next()); - - QList datePropertyManagers = manager->findChildren(); - QListIterator itDate(datePropertyManagers); - while (itDate.hasNext()) - d_ptr->m_dateEditFactory->removePropertyManager(itDate.next()); - - QList timePropertyManagers = manager->findChildren(); - QListIterator itTime(timePropertyManagers); - while (itTime.hasNext()) - d_ptr->m_timeEditFactory->removePropertyManager(itTime.next()); - - QList dateTimePropertyManagers = manager->findChildren(); - QListIterator itDateTime(dateTimePropertyManagers); - while (itDateTime.hasNext()) - d_ptr->m_dateTimeEditFactory->removePropertyManager(itDateTime.next()); - - QList keySequencePropertyManagers = manager->findChildren(); - QListIterator itKeySequence(keySequencePropertyManagers); - while (itKeySequence.hasNext()) - d_ptr->m_keySequenceEditorFactory->removePropertyManager(itKeySequence.next()); - - QList charPropertyManagers = manager->findChildren(); - QListIterator itChar(charPropertyManagers); - while (itChar.hasNext()) - d_ptr->m_charEditorFactory->removePropertyManager(itChar.next()); - - QList pointPropertyManagers = manager->findChildren(); - QListIterator itPoint(pointPropertyManagers); - while (itPoint.hasNext()) - d_ptr->m_spinBoxFactory->removePropertyManager(itPoint.next()->subIntPropertyManager()); - - QList pointFPropertyManagers = manager->findChildren(); - QListIterator itPointF(pointFPropertyManagers); - while (itPointF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itPointF.next()->subDoublePropertyManager()); - - QList sizePropertyManagers = manager->findChildren(); - QListIterator itSize(sizePropertyManagers); - while (itSize.hasNext()) - d_ptr->m_spinBoxFactory->removePropertyManager(itSize.next()->subIntPropertyManager()); - - QList sizeFPropertyManagers = manager->findChildren(); - QListIterator itSizeF(sizeFPropertyManagers); - while (itSizeF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itSizeF.next()->subDoublePropertyManager()); - - QList rectPropertyManagers = manager->findChildren(); - QListIterator itRect(rectPropertyManagers); - while (itRect.hasNext()) - d_ptr->m_spinBoxFactory->removePropertyManager(itRect.next()->subIntPropertyManager()); - - QList rectFPropertyManagers = manager->findChildren(); - QListIterator itRectF(rectFPropertyManagers); - while (itRectF.hasNext()) - d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itRectF.next()->subDoublePropertyManager()); - - QList colorPropertyManagers = manager->findChildren(); - QListIterator itColor(colorPropertyManagers); - while (itColor.hasNext()) { - QtColorPropertyManager *manager = itColor.next(); - d_ptr->m_colorEditorFactory->removePropertyManager(manager); - d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); - } - - QList enumPropertyManagers = manager->findChildren(); - QListIterator itEnum(enumPropertyManagers); - while (itEnum.hasNext()) - d_ptr->m_comboBoxFactory->removePropertyManager(itEnum.next()); - - QList fontPropertyManagers = manager->findChildren(); - QListIterator itFont(fontPropertyManagers); - while (itFont.hasNext()) { - QtFontPropertyManager *manager = itFont.next(); - d_ptr->m_fontEditorFactory->removePropertyManager(manager); - d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); - d_ptr->m_comboBoxFactory->removePropertyManager(manager->subEnumPropertyManager()); - d_ptr->m_checkBoxFactory->removePropertyManager(manager->subBoolPropertyManager()); - } - - QList cursorPropertyManagers = manager->findChildren(); - QListIterator itCursor(cursorPropertyManagers); - while (itCursor.hasNext()) - d_ptr->m_cursorEditorFactory->removePropertyManager(itCursor.next()); - - QList flagPropertyManagers = manager->findChildren(); - QListIterator itFlag(flagPropertyManagers); - while (itFlag.hasNext()) - d_ptr->m_checkBoxFactory->removePropertyManager(itFlag.next()->subBoolPropertyManager()); -} - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -#include "moc_qtvariantproperty.cpp" diff --git a/src/qtpropertybrowser/src/qtvariantproperty.h b/src/qtpropertybrowser/src/qtvariantproperty.h deleted file mode 100644 index 98e7085855..0000000000 --- a/src/qtpropertybrowser/src/qtvariantproperty.h +++ /dev/null @@ -1,188 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#ifndef QTVARIANTPROPERTY_H -#define QTVARIANTPROPERTY_H - -#include "qtpropertybrowser.h" -#include -#include - -#if QT_VERSION >= 0x040400 -QT_BEGIN_NAMESPACE -#endif - -typedef QMap QtIconMap; - -class QtVariantPropertyManager; -class QtVariantPropertyPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtVariantProperty : public QtProperty -{ -public: - ~QtVariantProperty(); - QVariant value() const; - QVariant attributeValue(const QString &attribute) const; - int valueType() const; - int propertyType() const; - - void setValue(const QVariant &value); - void setAttribute(const QString &attribute, const QVariant &value); -protected: - explicit QtVariantProperty(QtVariantPropertyManager *manager); -private: - friend class QtVariantPropertyManager; - QtVariantPropertyPrivate *d_ptr; -}; - -class QtVariantPropertyManagerPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtVariantPropertyManager : public QtAbstractPropertyManager -{ - Q_OBJECT -public: - QtVariantPropertyManager(QObject *parent = 0); - ~QtVariantPropertyManager(); - - virtual QtVariantProperty *addProperty(int propertyType, const QString &name = QString()); - - int propertyType(const QtProperty *property) const; - int valueType(const QtProperty *property) const; - QtVariantProperty *variantProperty(const QtProperty *property) const; - - virtual bool isPropertyTypeSupported(int propertyType) const; - virtual int valueType(int propertyType) const; - virtual QStringList attributes(int propertyType) const; - virtual int attributeType(int propertyType, const QString &attribute) const; - - virtual QVariant value(const QtProperty *property) const; - virtual QVariant attributeValue(const QtProperty *property, const QString &attribute) const; - - static int enumTypeId(); - static int flagTypeId(); - static int groupTypeId(); - static int iconMapTypeId(); -public Q_SLOTS: - virtual void setValue(QtProperty *property, const QVariant &val); - virtual void setAttribute(QtProperty *property, - const QString &attribute, const QVariant &value); -Q_SIGNALS: - void valueChanged(QtProperty *property, const QVariant &val); - void attributeChanged(QtProperty *property, - const QString &attribute, const QVariant &val); -protected: - virtual bool hasValue(const QtProperty *property) const; - QString valueText(const QtProperty *property) const; - QIcon valueIcon(const QtProperty *property) const; - virtual void initializeProperty(QtProperty *property); - virtual void uninitializeProperty(QtProperty *property); - virtual QtProperty *createProperty(); -private: - QtVariantPropertyManagerPrivate *d_ptr; - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) - Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) - Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QString &)) - Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegularExpression &)) - Q_PRIVATE_SLOT(d_func(), void slotEchoModeChanged(QtProperty *, int)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDate &)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QDate &, const QDate &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QTime &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDateTime &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QKeySequence &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QChar &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QLocale &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPoint &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPointF &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSize &)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSize &, const QSize &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizeF &)) - Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRect &)) - Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRect &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRectF &)) - Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRectF &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QColor &)) - Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, const QStringList &)) - Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, const QMap &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizePolicy &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QFont &)) - Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QCursor &)) - Q_PRIVATE_SLOT(d_func(), void slotFlagNamesChanged(QtProperty *, const QStringList &)) - Q_PRIVATE_SLOT(d_func(), void slotReadOnlyChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotTextVisibleChanged(QtProperty *, bool)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *)) - Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, QtProperty *)) - Q_DECLARE_PRIVATE(QtVariantPropertyManager) - Q_DISABLE_COPY(QtVariantPropertyManager) -}; - -class QtVariantEditorFactoryPrivate; - -class QT_QTPROPERTYBROWSER_EXPORT QtVariantEditorFactory : public QtAbstractEditorFactory -{ - Q_OBJECT -public: - QtVariantEditorFactory(QObject *parent = 0); - ~QtVariantEditorFactory(); -protected: - void connectPropertyManager(QtVariantPropertyManager *manager); - QWidget *createEditor(QtVariantPropertyManager *manager, QtProperty *property, - QWidget *parent); - void disconnectPropertyManager(QtVariantPropertyManager *manager); -private: - QtVariantEditorFactoryPrivate *d_ptr; - Q_DECLARE_PRIVATE(QtVariantEditorFactory) - Q_DISABLE_COPY(QtVariantEditorFactory) -}; - -#if QT_VERSION >= 0x040400 -QT_END_NAMESPACE -#endif - -Q_DECLARE_METATYPE(QIcon) -Q_DECLARE_METATYPE(QtIconMap) -#endif From 66ec4aaac1371e61e10a61618eb53e7369b7a5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 11 Nov 2024 12:05:25 +0100 Subject: [PATCH 63/78] Allow using Tab to change focus in the Properties view --- src/tiled/propertiesdock.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tiled/propertiesdock.cpp b/src/tiled/propertiesdock.cpp index 36cde21f64..58607a0561 100644 --- a/src/tiled/propertiesdock.cpp +++ b/src/tiled/propertiesdock.cpp @@ -21,8 +21,9 @@ #include "propertiesdock.h" #include "propertieswidget.h" +#include "varianteditor.h" -#include +#include namespace Tiled { @@ -56,6 +57,14 @@ bool PropertiesDock::event(QEvent *event) case QEvent::LanguageChange: retranslateUi(); break; + case QEvent::ShortcutOverride: + if (static_cast(event)->key() == Qt::Key_Tab) { + if (!mPropertiesWidget->propertiesView()->hasFocus()) { + event->accept(); + return true; + } + } + break; default: break; } From 8c767d0ac729717d6351e1bdcfe7055b8423ee27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 11 Nov 2024 12:33:30 +0100 Subject: [PATCH 64/78] Fixed some issues when changing map properties A lot of places were still relying on the mapChanged signal, but they should use mapResized or the MapChangeEvent since 2177fac48053757b45462c042fc2d98da94. --- src/tiled/mapdocument.h | 4 ++-- src/tiled/mapdocumentactionhandler.cpp | 9 +++++++-- src/tiled/mapitem.cpp | 23 ++++++++++++++++++++++- src/tiled/objectselectionitem.cpp | 11 +++-------- src/tiled/objectselectionitem.h | 1 - src/tiled/objectselectiontool.cpp | 14 +++++++------- src/tiled/propertieswidget.cpp | 2 +- src/tiled/resizemap.cpp | 2 +- 8 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/tiled/mapdocument.h b/src/tiled/mapdocument.h index 6b44705ff8..803de30937 100644 --- a/src/tiled/mapdocument.h +++ b/src/tiled/mapdocument.h @@ -322,9 +322,9 @@ class TILED_EDITOR_EXPORT MapDocument final : public Document void mapObjectPicked(MapObject *object); /** - * Emitted when the map size or its tile size changes. + * Emitted when the map size size changes. */ - void mapChanged(); + void mapResized(); void layerAdded(Layer *layer); void layerAboutToBeRemoved(GroupLayer *parentLayer, int index); diff --git a/src/tiled/mapdocumentactionhandler.cpp b/src/tiled/mapdocumentactionhandler.cpp index 91f3c625ce..4b5b4666f0 100644 --- a/src/tiled/mapdocumentactionhandler.cpp +++ b/src/tiled/mapdocumentactionhandler.cpp @@ -24,6 +24,7 @@ #include "actionmanager.h" #include "addremovelayer.h" #include "addremovemapobject.h" +#include "changeevents.h" #include "changeselectedarea.h" #include "clipboardmanager.h" #include "documentmanager.h" @@ -297,8 +298,12 @@ void MapDocumentActionHandler::setMapDocument(MapDocument *mapDocument) this, &MapDocumentActionHandler::updateActions); connect(mapDocument, &MapDocument::selectedObjectsChanged, this, &MapDocumentActionHandler::updateActions); - connect(mapDocument, &MapDocument::mapChanged, - this, &MapDocumentActionHandler::updateActions); + connect(mapDocument, &MapDocument::changed, + this, [this] (const ChangeEvent &change) { + if (change.type == ChangeEvent::MapChanged) + if (static_cast(change).property == Map::InfiniteProperty) + updateActions(); + }); } } diff --git a/src/tiled/mapitem.cpp b/src/tiled/mapitem.cpp index 034a1c0404..27f0f1f4a9 100644 --- a/src/tiled/mapitem.cpp +++ b/src/tiled/mapitem.cpp @@ -155,7 +155,7 @@ MapItem::MapItem(const MapDocumentPtr &mapDocument, DisplayMode displayMode, connect(prefs, &Preferences::backgroundFadeColorChanged, this, [this] (QColor color) { mDarkRectangle->setBrush(color); }); connect(mapDocument.data(), &Document::changed, this, &MapItem::documentChanged); - connect(mapDocument.data(), &MapDocument::mapChanged, this, &MapItem::mapChanged); + connect(mapDocument.data(), &MapDocument::mapResized, this, &MapItem::mapChanged); connect(mapDocument.data(), &MapDocument::regionChanged, this, &MapItem::repaintRegion); connect(mapDocument.data(), &MapDocument::tileLayerChanged, this, &MapItem::tileLayerChanged); connect(mapDocument.data(), &MapDocument::layerAdded, this, &MapItem::layerAdded); @@ -391,6 +391,27 @@ void MapItem::documentChanged(const ChangeEvent &change) break; } + case ChangeEvent::MapChanged: { + auto &mapChange = static_cast(change); + switch (mapChange.property) { + case Map::TileSizeProperty: + case Map::InfiniteProperty: + case Map::HexSideLengthProperty: + case Map::StaggerAxisProperty: + case Map::StaggerIndexProperty: + case Map::ParallaxOriginProperty: + case Map::OrientationProperty: + mapChanged(); + break; + case Map::RenderOrderProperty: + case Map::BackgroundColorProperty: + case Map::LayerDataFormatProperty: + case Map::CompressionLevelProperty: + case Map::ChunkSizeProperty: + break; + } + break; + } case ChangeEvent::LayerChanged: layerChanged(static_cast(change)); break; diff --git a/src/tiled/objectselectionitem.cpp b/src/tiled/objectselectionitem.cpp index 38c033356e..3f9d9ccf91 100644 --- a/src/tiled/objectselectionitem.cpp +++ b/src/tiled/objectselectionitem.cpp @@ -280,9 +280,6 @@ ObjectSelectionItem::ObjectSelectionItem(MapDocument *mapDocument, connect(mapDocument, &MapDocument::aboutToBeSelectedObjectsChanged, this, &ObjectSelectionItem::aboutToBeSelectedObjectsChanged); - connect(mapDocument, &MapDocument::mapChanged, - this, &ObjectSelectionItem::mapChanged); - connect(mapDocument, &MapDocument::layerAdded, this, &ObjectSelectionItem::layerAdded); @@ -403,6 +400,9 @@ void ObjectSelectionItem::changeEvent(const ChangeEvent &event) } break; } + case ChangeEvent::MapChanged: + updateItemPositions(); + break; case ChangeEvent::LayerChanged: layerChanged(static_cast(event)); break; @@ -493,11 +493,6 @@ void ObjectSelectionItem::hoveredMapObjectChanged(MapObject *object, } } -void ObjectSelectionItem::mapChanged() -{ - updateItemPositions(); -} - static void collectObjects(const GroupLayer &groupLayer, QList &objects, bool onlyVisibleLayers = false) { for (Layer *layer : groupLayer) { diff --git a/src/tiled/objectselectionitem.h b/src/tiled/objectselectionitem.h index e67fbe088b..ce29bf8607 100644 --- a/src/tiled/objectselectionitem.h +++ b/src/tiled/objectselectionitem.h @@ -95,7 +95,6 @@ class ObjectSelectionItem : public QGraphicsObject void selectedObjectsChanged(); void aboutToBeSelectedObjectsChanged(); void hoveredMapObjectChanged(MapObject *object, MapObject *previous); - void mapChanged(); void layerAdded(Layer *layer); void layerAboutToBeRemoved(GroupLayer *parentLayer, int index); void layerChanged(const LayerChangeEvent &event); diff --git a/src/tiled/objectselectiontool.cpp b/src/tiled/objectselectiontool.cpp index 758eb7a113..e1b67c8c31 100644 --- a/src/tiled/objectselectiontool.cpp +++ b/src/tiled/objectselectiontool.cpp @@ -54,7 +54,6 @@ #include #include -#include using namespace Tiled; @@ -335,8 +334,10 @@ ObjectSelectionTool::ObjectSelectionTool(QObject *parent) else mSelectContained->setChecked(true); - connect(mSelectIntersected, &QAction::triggered, [this] { setSelectionMode(Qt::IntersectsItemShape); }); - connect(mSelectContained, &QAction::triggered, [this] { setSelectionMode(Qt::ContainsItemShape); }); + connect(mSelectIntersected, &QAction::triggered, + this, [this] { setSelectionMode(Qt::IntersectsItemShape); }); + connect(mSelectContained, &QAction::triggered, + this, [this] { setSelectionMode(Qt::ContainsItemShape); }); for (int i = 0; i < CornerAnchorCount; ++i) mRotateHandles[i] = new RotateHandle(static_cast(i)); @@ -366,8 +367,6 @@ void ObjectSelectionTool::activate(MapScene *scene) updateHandlesAndOrigin(); - connect(mapDocument(), &MapDocument::mapChanged, - this, &ObjectSelectionTool::updateHandlesAndOrigin); connect(mapDocument(), &MapDocument::selectedObjectsChanged, this, &ObjectSelectionTool::updateHandlesAndOrigin); connect(mapDocument(), &MapDocument::tilesetTilePositioningChanged, @@ -390,8 +389,6 @@ void ObjectSelectionTool::deactivate(MapScene *scene) for (ResizeHandle *handle : mResizeHandles) scene->removeItem(handle); - disconnect(mapDocument(), &MapDocument::mapChanged, - this, &ObjectSelectionTool::updateHandlesAndOrigin); disconnect(mapDocument(), &MapDocument::selectedObjectsChanged, this, &ObjectSelectionTool::updateHandlesAndOrigin); disconnect(mapDocument(), &MapDocument::tilesetTilePositioningChanged, @@ -779,6 +776,9 @@ void ObjectSelectionTool::changeEvent(const ChangeEvent &event) return; switch (event.type) { + case ChangeEvent::MapChanged: + updateHandlesAndOrigin(); + break; case ChangeEvent::LayerChanged: if (static_cast(event).properties & LayerChangeEvent::PositionProperties) updateHandlesAndOrigin(); diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 38d0188171..cc31fd2554 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -638,7 +638,7 @@ class MapSizeProperty : public SizeProperty parent) , mMapDocument(mapDocument) { - connect(mMapDocument, &MapDocument::mapChanged, + connect(mMapDocument, &MapDocument::mapResized, this, &Property::valueChanged); } diff --git a/src/tiled/resizemap.cpp b/src/tiled/resizemap.cpp index 9cb289207f..3469dc8ecd 100644 --- a/src/tiled/resizemap.cpp +++ b/src/tiled/resizemap.cpp @@ -56,7 +56,7 @@ void ResizeMap::swapSize() map->setHeight(mSize.height()); mSize = oldSize; - emit mMapDocument->mapChanged(); + emit mMapDocument->mapResized(); } } // namespace Tiled From ccf704dd19e889ec41f363c805afb1a293ea5a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 11 Nov 2024 15:12:52 +0100 Subject: [PATCH 65/78] Focus new custom properties or class members when adding them When adding custom class properties, they are expanded with the first member focused. --- src/tiled/propertieswidget.cpp | 3 ++- src/tiled/propertytypeseditor.cpp | 18 ++++----------- src/tiled/propertytypeseditor.h | 1 - src/tiled/varianteditor.cpp | 37 +++++++++++++++++++++++++++++++ src/tiled/varianteditor.h | 4 ++++ src/tiled/variantmapproperty.h | 6 +++++ 6 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index cc31fd2554..86559141ac 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2538,7 +2538,8 @@ void PropertiesWidget::addProperty(const QString &name, const QVariant &value) name, value)); } - // mPropertyBrowser->editCustomProperty(name); + if (auto property = mCustomProperties->property(name)) + mPropertyBrowser->focusProperty(property); } void PropertiesWidget::removeProperties() diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 38d030178c..9a1906015f 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -572,6 +572,8 @@ void PropertyTypesEditor::openAddMemberDialog() if (dialog.exec() == AddPropertyDialog::Accepted) addMember(dialog.propertyName(), QVariant(dialog.propertyValue())); + + activateWindow(); } void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) @@ -593,21 +595,9 @@ void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) applyMemberToSelectedType(name, value); updateDetails(); - editMember(name); -} -void PropertyTypesEditor::editMember(const QString &name) -{ - // todo: VariantEditor needs to support focusing the widget of a specific property -#if 0 - QtVariantProperty *property = mPropertiesHelper->property(name); - if (!property) - return; - - const QList propertyItems = mMembersView->items(property); - if (!propertyItems.isEmpty()) - mMembersView->editItem(propertyItems.first()); -#endif + if (auto property = mMembersProperty->property(name)) + mMembersEditor->focusProperty(property); } void PropertyTypesEditor::removeMember() diff --git a/src/tiled/propertytypeseditor.h b/src/tiled/propertytypeseditor.h index 27870c253a..d3eb4aeefa 100644 --- a/src/tiled/propertytypeseditor.h +++ b/src/tiled/propertytypeseditor.h @@ -104,7 +104,6 @@ class PropertyTypesEditor : public QDialog void openClassOfPopup(); void openAddMemberDialog(); void addMember(const QString &name, const QVariant &value = QVariant()); - void editMember(const QString &name); void removeMember(); void renameMember(const QString &name); void renameMemberTo(const QString &oldName, const QString &name); diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index f4caec5bce..5f74ebda1c 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -626,6 +626,38 @@ void VariantEditor::removeProperty(Property *property) property->disconnect(this); } +/** + * Focuses the editor for the given property. Makes sure any parent group + * properties are expanded. + * + * When the given property is a group property, the group property is expanded + * and the first child property is focused. + */ +bool VariantEditor::focusProperty(Property *property) +{ + for (auto it = m_propertyWidgets.constBegin(); it != m_propertyWidgets.constEnd(); ++it) { + auto &widgets = it.value(); + + if (it.key() == property) { + if (widgets.editor) + widgets.editor->setFocus(); + else if (auto groupProperty = qobject_cast(it.key())) { + groupProperty->setExpanded(true); + if (widgets.children && !groupProperty->subProperties().isEmpty()) + widgets.children->focusProperty(groupProperty->subProperties().first()); + } + return true; + } else if (auto groupProperty = qobject_cast(it.key())) { + if (widgets.children && widgets.children->focusProperty(property)) { + groupProperty->setExpanded(true); + return true; + } + } + } + + return false; +} + void VariantEditor::setLevel(int level) { m_level = level; @@ -1008,6 +1040,11 @@ VariantEditorView::VariantEditorView(QWidget *parent) setWidget(scrollWidget); } +void VariantEditorView::focusProperty(Property *property) +{ + m_editor->focusProperty(property); +} + } // namespace Tiled #include "moc_varianteditor.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index f1c9c2b84c..5fb0329b70 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -504,6 +504,8 @@ class VariantEditor : public QWidget void insertProperty(int index, Property *property); void removeProperty(Property *property); + bool focusProperty(Property *property); + void setLevel(int level); private: @@ -553,6 +555,8 @@ class VariantEditorView : public QScrollArea void addProperty(Property *property) { m_editor->addProperty(property); } + void focusProperty(Property *property); + private: VariantEditor *m_editor; }; diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h index 5219036f97..df0e144072 100644 --- a/src/tiled/variantmapproperty.h +++ b/src/tiled/variantmapproperty.h @@ -39,6 +39,8 @@ class VariantMapProperty : public GroupProperty const QVariantMap &value() const { return mValue; } + Property *property(const QString &name) const; + signals: void memberValueChanged(const QStringList &path, const QVariant &value); void renameRequested(const QString &name); @@ -85,5 +87,9 @@ class VariantMapProperty : public GroupProperty QSet mExpandedProperties; }; +inline Property *VariantMapProperty::property(const QString &name) const +{ + return mPropertyMap.value(name); +} } // namespace Tiled From 5fefeba5371202ba96efa2af6b3978c5c1d251e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 13 Nov 2024 17:30:33 +0100 Subject: [PATCH 66/78] Made Add Property action use inline widgets Use a special property that creates a name edit as label and a type combo box as editor, instead of a modal Add Property dialog. VariantEditorView::focusProperty now makes sure that the focused property widget is visible. The "add value" property is automatically removed when it loses focus. To achieve this, each property now gets its own parent widget rather than just having its widgets added to a nested horizontal layout. This way, we can check whether the focus remains within the same parent widget. The Add button has Qt::StrongFocus policy now, otherwise the focus would be lost to the properties view when clicking it. --- src/tiled/addpropertydialog.cpp | 124 -------------- src/tiled/addpropertydialog.h | 54 ------ src/tiled/addpropertydialog.ui | 103 ----------- src/tiled/libtilededitor.qbs | 3 - src/tiled/propertieswidget.cpp | 26 ++- src/tiled/propertieswidget.h | 5 +- src/tiled/propertyeditorwidgets.cpp | 7 +- src/tiled/propertyeditorwidgets.h | 2 +- src/tiled/propertytypeseditor.cpp | 21 ++- src/tiled/propertytypeseditor.h | 3 + src/tiled/varianteditor.cpp | 253 +++++++++++++++++----------- src/tiled/varianteditor.h | 31 ++-- src/tiled/variantmapproperty.cpp | 163 +++++++++++++++++- src/tiled/variantmapproperty.h | 49 ++++++ 14 files changed, 432 insertions(+), 412 deletions(-) delete mode 100644 src/tiled/addpropertydialog.cpp delete mode 100644 src/tiled/addpropertydialog.h delete mode 100644 src/tiled/addpropertydialog.ui diff --git a/src/tiled/addpropertydialog.cpp b/src/tiled/addpropertydialog.cpp deleted file mode 100644 index 230ba545ad..0000000000 --- a/src/tiled/addpropertydialog.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * addpropertydialog.cpp - * Copyright 2015, CaptainFrog - * Copyright 2016, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "addpropertydialog.h" -#include "ui_addpropertydialog.h" - -#include "documentmanager.h" -#include "object.h" -#include "preferences.h" -#include "properties.h" -#include "propertytypesmodel.h" -#include "session.h" -#include "utils.h" - -#include - -using namespace Tiled; - -namespace session { -static SessionOption propertyType { "property.type", QStringLiteral("string") }; -} // namespace session - -AddPropertyDialog::AddPropertyDialog(QWidget *parent) - : QDialog(parent) - , mUi(new Ui::AddPropertyDialog) -{ - initialize(nullptr); -} - -AddPropertyDialog::AddPropertyDialog(const ClassPropertyType *parentClassType, QWidget *parent) - : QDialog(parent) - , mUi(new Ui::AddPropertyDialog) -{ - initialize(parentClassType); -} - -void AddPropertyDialog::initialize(const Tiled::ClassPropertyType *parentClassType) -{ - mUi->setupUi(this); - resize(Utils::dpiScaled(size())); - - const QIcon plain(QStringLiteral("://images/scalable/property-type-plain.svg")); - - // Add possible types from QVariant - mUi->typeBox->addItem(plain, typeToName(QMetaType::Bool), false); - mUi->typeBox->addItem(plain, typeToName(QMetaType::QColor), QColor()); - mUi->typeBox->addItem(plain, typeToName(QMetaType::Double), 0.0); - mUi->typeBox->addItem(plain, typeToName(filePathTypeId()), QVariant::fromValue(FilePath())); - mUi->typeBox->addItem(plain, typeToName(QMetaType::Int), 0); - mUi->typeBox->addItem(plain, typeToName(objectRefTypeId()), QVariant::fromValue(ObjectRef())); - mUi->typeBox->addItem(plain, typeToName(QMetaType::QString), QString()); - - for (const auto propertyType : Object::propertyTypes()) { - // Avoid suggesting the creation of circular dependencies between types - if (parentClassType && !parentClassType->canAddMemberOfType(propertyType)) - continue; - - // Avoid suggesting classes not meant to be used as property value - if (propertyType->isClass()) - if (!static_cast(propertyType)->isPropertyValueType()) - continue; - - const QVariant var = propertyType->wrap(propertyType->defaultValue()); - const QIcon icon = PropertyTypesModel::iconForPropertyType(propertyType->type); - mUi->typeBox->addItem(icon, propertyType->name, var); - } - - mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - // Restore previously used type - mUi->typeBox->setCurrentText(session::propertyType); - - connect(mUi->name, &QLineEdit::textChanged, - this, &AddPropertyDialog::nameChanged); - connect(mUi->typeBox, &QComboBox::currentTextChanged, - this, &AddPropertyDialog::typeChanged); - - mUi->name->setFocus(); -} - -AddPropertyDialog::~AddPropertyDialog() -{ - delete mUi; -} - -QString AddPropertyDialog::propertyName() const -{ - return mUi->name->text(); -} - -QVariant AddPropertyDialog::propertyValue() const -{ - return mUi->typeBox->currentData(); -} - -void AddPropertyDialog::nameChanged(const QString &text) -{ - mUi->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); -} - -void AddPropertyDialog::typeChanged(const QString &text) -{ - session::propertyType = text; -} - -#include "moc_addpropertydialog.cpp" diff --git a/src/tiled/addpropertydialog.h b/src/tiled/addpropertydialog.h deleted file mode 100644 index ba36977958..0000000000 --- a/src/tiled/addpropertydialog.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * addpropertydialog.h - * Copyright 2015, CaptainFrog - * Copyright 2016, Thorbjørn Lindeijer - * - * This file is part of Tiled. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once - -#include -#include - -namespace Tiled { -class ClassPropertyType; -} - -namespace Ui { -class AddPropertyDialog; -} - -class AddPropertyDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AddPropertyDialog(QWidget *parent = nullptr); - AddPropertyDialog(const Tiled::ClassPropertyType *parentClassType, QWidget *parent = nullptr); - ~AddPropertyDialog() override; - - QString propertyName() const; - QVariant propertyValue() const; - -private: - void initialize(const Tiled::ClassPropertyType *parentClassType); - - void nameChanged(const QString &text); - void typeChanged(const QString &text); - - Ui::AddPropertyDialog *mUi; -}; diff --git a/src/tiled/addpropertydialog.ui b/src/tiled/addpropertydialog.ui deleted file mode 100644 index 1b927c33b5..0000000000 --- a/src/tiled/addpropertydialog.ui +++ /dev/null @@ -1,103 +0,0 @@ - - - AddPropertyDialog - - - - 0 - 0 - 320 - 134 - - - - - 0 - 0 - - - - Add Property - - - - QLayout::SetMinAndMaxSize - - - - - - - - Qt::Horizontal - - - - 214 - 18 - - - - - - - - Property name - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - typeBox - name - - - - - buttonBox - accepted() - AddPropertyDialog - accept() - - - 302 - 96 - - - 157 - 105 - - - - - buttonBox - rejected() - AddPropertyDialog - reject() - - - 338 - 96 - - - 286 - 105 - - - - - diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 328af5f4d7..6d73e57a8f 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -94,9 +94,6 @@ DynamicLibrary { "actionmanager.h", "actionsearch.cpp", "actionsearch.h", - "addpropertydialog.cpp", - "addpropertydialog.h", - "addpropertydialog.ui", "addremovelayer.cpp", "addremovelayer.h", "addremovemapobject.cpp", diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 86559141ac..0cd8f74c09 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -21,7 +21,6 @@ #include "propertieswidget.h" #include "actionmanager.h" -#include "addpropertydialog.h" #include "changeimagelayerproperty.h" #include "changelayer.h" #include "changemapobject.h" @@ -2166,18 +2165,20 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mActionAddProperty->setEnabled(false); mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); connect(mActionAddProperty, &QAction::triggered, - this, &PropertiesWidget::openAddPropertyDialog); + this, &PropertiesWidget::showAddValueProperty); mActionRemoveProperty = new QAction(this); mActionRemoveProperty->setEnabled(false); mActionRemoveProperty->setIcon(QIcon(QLatin1String(":/images/16/remove.png"))); mActionRemoveProperty->setShortcuts(QKeySequence::Delete); + mActionRemoveProperty->setPriority(QAction::LowPriority); connect(mActionRemoveProperty, &QAction::triggered, this, &PropertiesWidget::removeProperties); mActionRenameProperty = new QAction(this); mActionRenameProperty->setEnabled(false); mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); + mActionRenameProperty->setPriority(QAction::LowPriority); // connect(mActionRenameProperty, &QAction::triggered, // this, &PropertiesWidget::renameProperty); @@ -2188,6 +2189,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) QToolBar *toolBar = new QToolBar; toolBar->setFloatable(false); toolBar->setMovable(false); + toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolBar->setIconSize(Utils::smallIconSize()); toolBar->addAction(mActionAddProperty); toolBar->addAction(mActionRemoveProperty); @@ -2516,11 +2518,23 @@ void PropertiesWidget::pasteProperties() } } -void PropertiesWidget::openAddPropertyDialog() +void PropertiesWidget::showAddValueProperty() { - AddPropertyDialog dialog(mPropertyBrowser); - if (dialog.exec() == AddPropertyDialog::Accepted) - addProperty(dialog.propertyName(), dialog.propertyValue()); + if (!mAddValueProperty) { + mAddValueProperty = new AddValueProperty(mCustomProperties); + + connect(mAddValueProperty, &Property::addRequested, this, [this] { + addProperty(mAddValueProperty->name(), mAddValueProperty->value()); + mCustomProperties->deleteProperty(mAddValueProperty); + }); + connect(mAddValueProperty, &Property::removeRequested, this, [this] { + mCustomProperties->deleteProperty(mAddValueProperty); + }); + + mCustomProperties->addProperty(mAddValueProperty); + } + + mPropertyBrowser->focusProperty(mAddValueProperty, VariantEditor::FocusLabel); } void PropertiesWidget::addProperty(const QString &name, const QVariant &value) diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 2c9a5f0432..050fa8cf95 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include class QScrollArea; @@ -29,6 +30,7 @@ namespace Tiled { class Object; +class AddValueProperty; class CustomProperties; class Document; class GroupProperty; @@ -73,7 +75,7 @@ public slots: void cutProperties(); bool copyProperties(); void pasteProperties(); - void openAddPropertyDialog(); + void showAddValueProperty(); void addProperty(const QString &name, const QVariant &value); void removeProperties(); void renameProperty(const QString &name); @@ -84,6 +86,7 @@ public slots: Document *mDocument = nullptr; ObjectProperties *mPropertiesObject = nullptr; CustomProperties *mCustomProperties = nullptr; + QPointer mAddValueProperty; QMap mExpandedStates; VariantEditorView *mPropertyBrowser; QAction *mActionAddProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 3bf358f9fb..ff95d55757 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -692,15 +692,18 @@ void ElidingLabel::paintEvent(QPaintEvent *) } -PropertyLabel::PropertyLabel(int level, QWidget *parent) +PropertyLabel::PropertyLabel(QWidget *parent) : ElidingLabel(parent) { setMinimumWidth(Utils::dpiScaled(50)); - setLevel(level); + updateContentMargins(); } void PropertyLabel::setLevel(int level) { + if (m_level == level) + return; + m_level = level; updateContentMargins(); } diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 3727d3bfa2..63721a98b6 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -309,7 +309,7 @@ class PropertyLabel : public ElidingLabel Q_OBJECT public: - PropertyLabel(int level, QWidget *parent = nullptr); + PropertyLabel(QWidget *parent = nullptr); void setLevel(int level); diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 9a1906015f..53fe0bde95 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -21,7 +21,6 @@ #include "propertytypeseditor.h" #include "ui_propertytypeseditor.h" -#include "addpropertydialog.h" #include "colorbutton.h" #include "objecttypes.h" #include "preferences.h" @@ -567,13 +566,23 @@ void PropertyTypesEditor::openAddMemberDialog() if (!propertyType || !propertyType->isClass()) return; - AddPropertyDialog dialog(static_cast(propertyType), this); - dialog.setWindowTitle(tr("Add Member")); + if (!mAddValueProperty) { + mAddValueProperty = new AddValueProperty(mMembersProperty); + mAddValueProperty->setPlaceholderText(tr("Member name")); + mAddValueProperty->setParentClassType(static_cast(propertyType)); - if (dialog.exec() == AddPropertyDialog::Accepted) - addMember(dialog.propertyName(), QVariant(dialog.propertyValue())); + connect(mAddValueProperty, &Property::addRequested, this, [this] { + addMember(mAddValueProperty->name(), mAddValueProperty->value()); + mMembersProperty->deleteProperty(mAddValueProperty); + }); + connect(mAddValueProperty, &Property::removeRequested, this, [this] { + mMembersProperty->deleteProperty(mAddValueProperty); + }); + + mMembersProperty->addProperty(mAddValueProperty); + } - activateWindow(); + mMembersEditor->focusProperty(mAddValueProperty, VariantEditor::FocusLabel); } void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) diff --git a/src/tiled/propertytypeseditor.h b/src/tiled/propertytypeseditor.h index d3eb4aeefa..afd4041eda 100644 --- a/src/tiled/propertytypeseditor.h +++ b/src/tiled/propertytypeseditor.h @@ -23,6 +23,7 @@ #include "propertytype.h" #include +#include class QCheckBox; class QComboBox; @@ -40,6 +41,7 @@ class PropertyTypesEditor; namespace Tiled { +class AddValueProperty; class ColorButton; class PropertyTypesModel; class VariantEditorView; @@ -145,6 +147,7 @@ class PropertyTypesEditor : public QDialog QPushButton *mClassOfButton = nullptr; VariantEditorView *mMembersEditor = nullptr; VariantMapProperty *mMembersProperty = nullptr; + QPointer mAddValueProperty; bool mSettingPrefPropertyTypes = false; bool mSettingName = false; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 5f74ebda1c..1af8dc8002 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -80,6 +82,29 @@ void Property::setActions(Actions actions) } } +QWidget *Property::createLabel(int level, QWidget *parent) +{ + auto label = new PropertyLabel(parent); + label->setLevel(level); + + connect(label, &PropertyLabel::contextMenuRequested, + this, &Property::contextMenuRequested); + + if (displayMode() != Property::DisplayMode::NoLabel) { + label->setText(name()); + label->setModified(isModified()); + label->setToolTip(toolTip()); + connect(this, &Property::nameChanged, label, &PropertyLabel::setText); + connect(this, &Property::toolTipChanged, label, &PropertyLabel::setToolTip); + connect(this, &Property::modifiedChanged, label, &PropertyLabel::setModified); + } + + if (displayMode() == Property::DisplayMode::Header) + label->setHeader(true); + + return label; +} + Property::DisplayMode GroupProperty::displayMode() const { if (name().isEmpty()) @@ -91,6 +116,18 @@ Property::DisplayMode GroupProperty::displayMode() const return DisplayMode::Default; } +QWidget *GroupProperty::createLabel(int level, QWidget *parent) +{ + auto label = static_cast(Property::createLabel(level, parent)); + label->setExpandable(true); + label->setExpanded(isExpanded()); + + connect(this, &GroupProperty::expandedChanged, label, &PropertyLabel::setExpanded); + connect(label, &PropertyLabel::toggled, this, &GroupProperty::setExpanded); + + return label; +} + void GroupProperty::setExpanded(bool expanded) { if (m_expanded != expanded) { @@ -580,9 +617,7 @@ void VariantEditor::clear() QHashIterator it(m_propertyWidgets); while (it.hasNext()) { it.next(); - auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.layout); - delete widgets.layout; + delete it.value().rowWidget; it.key()->disconnect(this); } @@ -595,7 +630,7 @@ void VariantEditor::clear() */ void VariantEditor::addProperty(Property *property) { - m_layout->addLayout(createPropertyLayout(property)); + m_layout->addWidget(createPropertyWidget(property)); } /** @@ -604,7 +639,7 @@ void VariantEditor::addProperty(Property *property) */ void VariantEditor::insertProperty(int index, Property *property) { - m_layout->insertLayout(index, createPropertyLayout(property)); + m_layout->insertWidget(index, createPropertyWidget(property)); } /** @@ -615,47 +650,69 @@ void VariantEditor::removeProperty(Property *property) auto it = m_propertyWidgets.constFind(property); Q_ASSERT(it != m_propertyWidgets.constEnd()); - if (it != m_propertyWidgets.constEnd()) { - auto &widgets = it.value(); - Utils::deleteAllFromLayout(widgets.layout); - delete widgets.layout; + if (it == m_propertyWidgets.constEnd()) + return; + + // Immediately remove from layout, but delete later to avoid deleting + // widgets while they are still handling events. + auto rowWidget = it.value().rowWidget; + m_layout->removeWidget(rowWidget); + rowWidget->deleteLater(); - m_propertyWidgets.erase(it); + m_propertyWidgets.erase(it); + + // This appears to be necessary to avoid flickering due to relayouting + // not being done before the next paint. + QWidget *widget = this; + while (widget && widget->layout()) { + widget->layout()->activate(); + widget = widget->parentWidget(); } property->disconnect(this); } /** - * Focuses the editor for the given property. Makes sure any parent group - * properties are expanded. + * Focuses the editor or label for the given property. Makes sure any parent + * group properties are expanded. * * When the given property is a group property, the group property is expanded * and the first child property is focused. + * + * Returns the focused widget or nullptr if the property was not found. */ -bool VariantEditor::focusProperty(Property *property) +QWidget *VariantEditor::focusProperty(Property *property, FocusTarget target) { for (auto it = m_propertyWidgets.constBegin(); it != m_propertyWidgets.constEnd(); ++it) { auto &widgets = it.value(); if (it.key() == property) { - if (widgets.editor) + if (target == FocusEditor && widgets.editor) { widgets.editor->setFocus(); - else if (auto groupProperty = qobject_cast(it.key())) { + return widgets.editor; + } + if (target == FocusLabel && widgets.label) { + widgets.label->setFocus(); + return widgets.label; + } + if (auto groupProperty = qobject_cast(it.key())) { groupProperty->setExpanded(true); if (widgets.children && !groupProperty->subProperties().isEmpty()) - widgets.children->focusProperty(groupProperty->subProperties().first()); + widgets.children->focusProperty(groupProperty->subProperties().first(), target); + return widgets.children; } - return true; + return nullptr; } else if (auto groupProperty = qobject_cast(it.key())) { - if (widgets.children && widgets.children->focusProperty(property)) { - groupProperty->setExpanded(true); - return true; + if (widgets.children) { + if (auto w = widgets.children->focusProperty(property, target)) { + groupProperty->setExpanded(true); + return w; + } } } } - return false; + return nullptr; } void VariantEditor::setLevel(int level) @@ -667,8 +724,10 @@ void VariantEditor::setLevel(int level) setAutoFillBackground(m_level > 0); } -QLayout *VariantEditor::createPropertyLayout(Property *property) +QWidget *VariantEditor::createPropertyWidget(Property *property) { + Q_ASSERT(!m_propertyWidgets.contains(property)); + auto &widgets = m_propertyWidgets[property]; const auto displayMode = property->displayMode(); @@ -680,118 +739,109 @@ QLayout *VariantEditor::createPropertyLayout(Property *property) if (displayMode == Property::DisplayMode::ChildrenOnly) { if (auto groupProperty = qobject_cast(property)) { - widgets.childrenLayout = new QVBoxLayout; - widgets.layout = widgets.childrenLayout; + auto containerWidget = new QWidget(this); + widgets.childrenLayout = new QVBoxLayout(containerWidget); + widgets.childrenLayout->setContentsMargins(0, 0, 0, 0); setPropertyChildrenExpanded(groupProperty, true); - return widgets.layout; + widgets.rowWidget = containerWidget; + return widgets.rowWidget; } } - auto rowLayout = new QHBoxLayout; + auto rowWidget = new QWidget(this); + auto rowLayout = new QHBoxLayout(rowWidget); rowLayout->setSpacing(halfSpacing * 2); + rowLayout->setContentsMargins(0, 0, 0, 0); - widgets.layout = rowLayout; + widgets.rowWidget = rowWidget; if (displayMode == Property::DisplayMode::Separator) { - auto separator = new QFrame(this); + auto separator = new QFrame(rowWidget); rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); separator->setForegroundRole(QPalette::Mid); rowLayout->addWidget(separator); - return widgets.layout; + return widgets.rowWidget; } - widgets.label = new PropertyLabel(m_level, this); + widgets.label = property->createLabel(m_level, rowWidget); - connect(widgets.label, &PropertyLabel::contextMenuRequested, - property, &Property::contextMenuRequested); - - if (displayMode != Property::DisplayMode::NoLabel) { - widgets.label->setText(property->name()); - widgets.label->setModified(property->isModified()); - connect(property, &Property::modifiedChanged, widgets.label, &PropertyLabel::setModified); + if (displayMode != Property::DisplayMode::Header) { + if (isLeftToRight()) + rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); + else + rowLayout->setContentsMargins(halfSpacing * 2, halfSpacing, 0, halfSpacing); } - if (displayMode == Property::DisplayMode::Header) - widgets.label->setHeader(true); - else if (isLeftToRight()) - rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); - else - rowLayout->setContentsMargins(halfSpacing * 2, halfSpacing, 0, halfSpacing); - - rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); + if (widgets.label) + rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); - widgets.editorLayout = new QHBoxLayout; - widgets.editor = property->createEditor(this); + auto editorLayout = new QHBoxLayout; + widgets.editor = property->createEditor(rowWidget); if (widgets.editor) { widgets.editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - widgets.editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); - rowLayout->addLayout(widgets.editorLayout, EditorStretch); + editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); + rowLayout->addLayout(editorLayout, EditorStretch); } else { - rowLayout->addLayout(widgets.editorLayout, 0); + rowLayout->addLayout(editorLayout, 0); } - widgets.resetButton = new QToolButton(this); + widgets.resetButton = new QToolButton(rowWidget); widgets.resetButton->setToolTip(tr("Reset")); widgets.resetButton->setIcon(m_resetIcon); widgets.resetButton->setAutoRaise(true); widgets.resetButton->setEnabled(property->isModified()); Utils::setThemeIcon(widgets.resetButton, "edit-clear"); - widgets.editorLayout->addWidget(widgets.resetButton, 0, Qt::AlignTop); + editorLayout->addWidget(widgets.resetButton, 0, Qt::AlignTop); connect(widgets.resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); connect(property, &Property::modifiedChanged, widgets.resetButton, &QWidget::setEnabled); - widgets.removeButton = new QToolButton(this); + widgets.removeButton = new QToolButton(rowWidget); widgets.removeButton->setToolTip(tr("Remove")); widgets.removeButton->setIcon(m_removeIcon); widgets.removeButton->setAutoRaise(true); Utils::setThemeIcon(widgets.removeButton, "remove"); - widgets.editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); + editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); connect(widgets.removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); - widgets.addButton = new QToolButton(this); + widgets.addButton = new QToolButton(rowWidget); widgets.addButton->setToolTip(tr("Add")); widgets.addButton->setIcon(m_addIcon); widgets.addButton->setAutoRaise(true); + widgets.addButton->setFocusPolicy(Qt::StrongFocus); // needed for AddValueProperty Utils::setThemeIcon(widgets.addButton, "add"); - widgets.editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); + editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); if (auto groupProperty = qobject_cast(property)) { - widgets.childrenLayout = new QVBoxLayout; - widgets.childrenLayout->addLayout(rowLayout); - widgets.layout = widgets.childrenLayout; + auto containerWidget = new QWidget(this); + widgets.childrenLayout = new QVBoxLayout(containerWidget); + widgets.childrenLayout->setContentsMargins(0, 0, 0, 0); + widgets.childrenLayout->setSpacing(0); + widgets.childrenLayout->addWidget(rowWidget); - connect(groupProperty, &GroupProperty::expandedChanged, widgets.label, &PropertyLabel::setExpanded); - connect(widgets.label, &PropertyLabel::toggled, this, [=](bool expanded) { + connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { setPropertyChildrenExpanded(groupProperty, expanded); - groupProperty->setExpanded(expanded); }); - widgets.label->setExpandable(true); - widgets.label->setExpanded(groupProperty->isExpanded()); + setPropertyChildrenExpanded(groupProperty, groupProperty->isExpanded()); + + widgets.rowWidget = containerWidget; } updatePropertyEnabled(widgets, property->isEnabled()); - updatePropertyToolTip(widgets, property->toolTip()); updatePropertyActions(widgets, property->actions()); - connect(property, &Property::nameChanged, this, [=] (const QString &name) { - updatePropertyName(m_propertyWidgets[property], name); - }); connect(property, &Property::enabledChanged, this, [=] (bool enabled) { updatePropertyEnabled(m_propertyWidgets[property], enabled); }); - connect(property, &Property::toolTipChanged, this, [=] (const QString &toolTip) { - updatePropertyToolTip(m_propertyWidgets[property], toolTip); - }); connect(property, &Property::actionsChanged, this, [=] (Property::Actions actions) { updatePropertyActions(m_propertyWidgets[property], actions); }); - return widgets.layout; + return widgets.rowWidget; } void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded) @@ -801,11 +851,12 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo // Create the children editor on-demand if (expanded && !widgets.children) { const auto halfSpacing = Utils::dpiScaled(2); + const auto displayMode = groupProperty->displayMode(); - widgets.children = new VariantEditor(this); - if (widgets.label && widgets.label->isHeader()) + widgets.children = new VariantEditor(widgets.childrenLayout->parentWidget()); + if (widgets.label && displayMode == Property::DisplayMode::Header) widgets.children->setContentsMargins(0, halfSpacing, 0, halfSpacing); - if (groupProperty->displayMode() == Property::DisplayMode::Default) + if (displayMode == Property::DisplayMode::Default) widgets.children->setLevel(m_level + 1); widgets.children->setEnabled(groupProperty->isEnabled()); for (auto property : groupProperty->subProperties()) @@ -822,7 +873,7 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo // needed to avoid flickering when hiding the editor if (!expanded) { - QWidget *widget = this; + QWidget *widget = widgets.childrenLayout->parentWidget(); while (widget && widget->layout()) { widget->layout()->activate(); widget = widget->parentWidget(); @@ -831,12 +882,6 @@ void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bo } } -void VariantEditor::updatePropertyName(const PropertyWidgets &widgets, const QString &name) -{ - if (widgets.label) - widgets.label->setText(name); -} - void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) { if (widgets.label) @@ -847,19 +892,14 @@ void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool e widgets.children->setEnabled(enabled); } -void VariantEditor::updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip) +void VariantEditor::updatePropertyActions(const PropertyWidgets &widgets, + Property::Actions actions) { - if (widgets.label) - widgets.label->setToolTip(toolTip); - if (widgets.editor) - widgets.editor->setToolTip(toolTip); -} + widgets.resetButton->setVisible(actions.testFlag(Property::Action::Reset)); + widgets.removeButton->setVisible(actions.testFlag(Property::Action::Remove)); + widgets.addButton->setVisible(actions.testFlag(Property::Action::Add)); -void VariantEditor::updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions) -{ - widgets.resetButton->setVisible(actions & Property::Action::Reset); - widgets.removeButton->setVisible(actions & Property::Action::Remove); - widgets.addButton->setVisible(actions & Property::Action::Add); + widgets.addButton->setEnabled(!actions.testFlag(Property::Action::AddDisabled)); } @@ -1040,9 +1080,32 @@ VariantEditorView::VariantEditorView(QWidget *parent) setWidget(scrollWidget); } -void VariantEditorView::focusProperty(Property *property) +void VariantEditorView::focusProperty(Property *property, + VariantEditor::FocusTarget target) +{ + if (auto widget = m_editor->focusProperty(property, target)) { + if (widget->isVisible()) { + ensureWidgetVisible(widget); + } else { + // Install event filter to detect when widget becomes visible + widget->installEventFilter(this); + } + } +} + +bool VariantEditorView::eventFilter(QObject *watched, QEvent *event) { - m_editor->focusProperty(property); + if (event->type() == QEvent::Show) { + if (QPointer widget = qobject_cast(watched)) { + // Schedule after all pending events including layout + QMetaObject::invokeMethod(this, [=] { + if (widget) + ensureWidgetVisible(widget); + }, Qt::QueuedConnection); + widget->removeEventFilter(this); + } + } + return QScrollArea::eventFilter(watched, event); } } // namespace Tiled diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 5fb0329b70..0fe33c35e9 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -20,13 +20,10 @@ #pragma once -#include #include #include -#include #include #include -#include #include #include @@ -63,6 +60,7 @@ class Property : public QObject Reset = 0x01, Remove = 0x02, Add = 0x04, + AddDisabled = Add | 0x08, }; Q_DECLARE_FLAGS(Actions, Action) @@ -88,6 +86,7 @@ class Property : public QObject virtual DisplayMode displayMode() const { return DisplayMode::Default; } + virtual QWidget *createLabel(int level, QWidget *parent); virtual QWidget *createEditor(QWidget *parent) = 0; signals: @@ -152,6 +151,7 @@ class GroupProperty : public Property DisplayMode displayMode() const override; + QWidget *createLabel(int level, QWidget *parent) override; QWidget *createEditor(QWidget */* parent */) override { return nullptr; } void setHeader(bool header) { m_header = header; } @@ -504,7 +504,12 @@ class VariantEditor : public QWidget void insertProperty(int index, Property *property); void removeProperty(Property *property); - bool focusProperty(Property *property); + enum FocusTarget { + FocusLabel, + FocusEditor, + }; + + QWidget *focusProperty(Property *property, FocusTarget target); void setLevel(int level); @@ -514,10 +519,9 @@ class VariantEditor : public QWidget struct PropertyWidgets { - QLayout *layout = nullptr; - QHBoxLayout *editorLayout = nullptr; + QWidget *rowWidget = nullptr; QVBoxLayout *childrenLayout = nullptr; - PropertyLabel *label = nullptr; + QWidget *label = nullptr; QWidget *editor = nullptr; QToolButton *resetButton = nullptr; QToolButton *removeButton = nullptr; @@ -525,14 +529,13 @@ class VariantEditor : public QWidget VariantEditor *children = nullptr; }; - QLayout *createPropertyLayout(Property *property); + QWidget *createPropertyWidget(Property *property); void setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded); - void updatePropertyName(const PropertyWidgets &widgets, const QString &name); void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); - void updatePropertyToolTip(const PropertyWidgets &widgets, const QString &toolTip); - void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); + void updatePropertyActions(const PropertyWidgets &widgets, + Property::Actions actions); QIcon m_resetIcon; QIcon m_removeIcon; @@ -555,7 +558,11 @@ class VariantEditorView : public QScrollArea void addProperty(Property *property) { m_editor->addProperty(property); } - void focusProperty(Property *property); + void focusProperty(Property *property, + VariantEditor::FocusTarget target = VariantEditor::FocusEditor); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; private: VariantEditor *m_editor; diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index ab42d22446..41a41aa39b 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -23,13 +23,24 @@ #include "mapdocument.h" #include "objectrefedit.h" #include "preferences.h" +#include "propertyeditorwidgets.h" +#include "propertytypesmodel.h" +#include "session.h" #include "utils.h" +#include +#include +#include #include #include namespace Tiled { +namespace session { +static SessionOption propertyType { "property.type", QStringLiteral("string") }; +} // namespace session + + class ObjectRefProperty : public PropertyTemplate { Q_OBJECT @@ -340,15 +351,21 @@ void VariantMapProperty::removeMember(const QString &name) void VariantMapProperty::addMember(const QString &name, const QVariant &value) { - const auto oldValue = mValue.value(name, mSuggestions.value(name)); - - mValue.insert(name, value); + int index = 0; if (auto property = mPropertyMap.value(name)) { - const int index = indexOfProperty(property); - createOrUpdateProperty(index, name, oldValue, value); + index = indexOfProperty(property); + } else for (auto it = mValue.keyBegin(); it != mValue.keyEnd(); ++it) { + if (*it < name) + ++index; + else + break; } + const auto oldValue = mValue.value(name, mSuggestions.value(name)); + mValue.insert(name, value); + createOrUpdateProperty(index, name, oldValue, value); + emitMemberValueChanged({ name }, value); } @@ -460,6 +477,142 @@ void VariantMapProperty::memberContextMenuRequested(Property *property, const QS menu.exec(globalPos); } + +AddValueProperty::AddValueProperty(QObject *parent) + : Property(QString(), parent) + , m_plainTypeIcon(QStringLiteral("://images/scalable/property-type-plain.svg")) + , m_placeholderText(tr("Property name")) +{ + setActions(Action::AddDisabled); +} + +void AddValueProperty::setPlaceholderText(const QString &text) +{ + if (m_placeholderText == text) + return; + + m_placeholderText = text; + emit placeholderTextChanged(text); +} + +QWidget *AddValueProperty::createLabel(int level, QWidget *parent) +{ + constexpr int QLineEditPrivate_horizontalMargin = 2; + const int spacing = Utils::dpiScaled(3); + const int branchIndicatorWidth = Utils::dpiScaled(14); + const int indent = branchIndicatorWidth * (level + 1); + + auto nameEdit = new LineEdit(parent); + nameEdit->setText(name()); + nameEdit->setPlaceholderText(m_placeholderText); + + QStyleOptionFrame option; + option.initFrom(nameEdit); + const int frameWidth = nameEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, nameEdit); + const int nativeMargin = QLineEditPrivate_horizontalMargin + frameWidth; + + QMargins margins; + + if (parent->isLeftToRight()) + margins = QMargins(spacing + indent - nativeMargin, 0, spacing - nativeMargin, 0); + else + margins = QMargins(spacing - nativeMargin, 0, spacing + indent - nativeMargin, 0); + + nameEdit->setContentsMargins(margins); + + connect(nameEdit, &QLineEdit::textChanged, this, &Property::setName); + connect(nameEdit, &QLineEdit::returnPressed, this, [this] { + if (!name().isEmpty()) + emit addRequested(); + }); + connect(this, &Property::nameChanged, this, [=](const QString &name) { + setActions(name.isEmpty() ? Action::AddDisabled : Action::Add); + }); + connect(this, &AddValueProperty::placeholderTextChanged, + nameEdit, &QLineEdit::setPlaceholderText); + + nameEdit->installEventFilter(this); + + connect(qApp, &QApplication::focusChanged, nameEdit, [=] (QWidget *, QWidget *focusWidget) { + // Ignore focus in different windows (popups, dialogs, etc.) + if (!focusWidget || focusWidget->window() != parent->window()) + return; + + // Request removal if focus moved elsewhere + if (!parent->isAncestorOf(focusWidget)) + emit removeRequested(); + }); + + return nameEdit; +} + +QWidget *AddValueProperty::createEditor(QWidget *parent) +{ + // Create combo box with property types + auto typeBox = new ComboBox(parent); + + // Add possible types from QVariant + typeBox->addItem(m_plainTypeIcon, typeToName(QMetaType::Bool), false); + typeBox->addItem(m_plainTypeIcon, typeToName(QMetaType::QColor), QColor()); + typeBox->addItem(m_plainTypeIcon, typeToName(QMetaType::Double), 0.0); + typeBox->addItem(m_plainTypeIcon, typeToName(filePathTypeId()), QVariant::fromValue(FilePath())); + typeBox->addItem(m_plainTypeIcon, typeToName(QMetaType::Int), 0); + typeBox->addItem(m_plainTypeIcon, typeToName(objectRefTypeId()), QVariant::fromValue(ObjectRef())); + typeBox->addItem(m_plainTypeIcon, typeToName(QMetaType::QString), QString()); + + for (const auto propertyType : Object::propertyTypes()) { + // Avoid suggesting the creation of circular dependencies between types + if (m_parentClassType && !m_parentClassType->canAddMemberOfType(propertyType)) + continue; + + // Avoid suggesting classes not meant to be used as property value + if (propertyType->isClass()) + if (!static_cast(propertyType)->isPropertyValueType()) + continue; + + const QVariant var = propertyType->wrap(propertyType->defaultValue()); + const QIcon icon = PropertyTypesModel::iconForPropertyType(propertyType->type); + typeBox->addItem(icon, propertyType->name, var); + } + + // Restore previously used type + typeBox->setCurrentText(session::propertyType); + if (typeBox->currentIndex() == -1) + typeBox->setCurrentIndex(typeBox->findData(QString())); + + m_value = typeBox->currentData(); + + connect(typeBox, qOverload(&QComboBox::currentIndexChanged), this, [=](int index) { + m_value = typeBox->itemData(index); + session::propertyType = typeBox->currentText(); + }); + + typeBox->installEventFilter(this); + + return typeBox; +} + +bool AddValueProperty::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::KeyPress: { + // When Escape is pressed while the name edit or the type combo has + // focus, request the removal of this property. + auto keyEvent = static_cast(event); + bool isNameEdit = qobject_cast(watched); + bool isTypeCombo = qobject_cast(watched); + if ((isNameEdit || isTypeCombo) && keyEvent->key() == Qt::Key_Escape) { + emit removeRequested(); + return true; + } + break; + } + default: + break; + } + return false; +} + } // namespace Tiled #include "moc_variantmapproperty.cpp" diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h index df0e144072..f0d8d40f23 100644 --- a/src/tiled/variantmapproperty.h +++ b/src/tiled/variantmapproperty.h @@ -23,10 +23,15 @@ #include "propertytype.h" #include "varianteditor.h" +#include + namespace Tiled { class Document; +/** + * A property that creates child properties based on a QVariantMap value. + */ class VariantMapProperty : public GroupProperty { Q_OBJECT @@ -92,4 +97,48 @@ inline Property *VariantMapProperty::property(const QString &name) const return mPropertyMap.value(name); } + +/** + * A property that creates widgets for adding a value with a certain name and + * type. + */ +class AddValueProperty : public Property +{ + Q_OBJECT + +public: + AddValueProperty(QObject *parent = nullptr); + + void setPlaceholderText(const QString &text); + void setParentClassType(const ClassPropertyType *parentClassType); + + QVariant value() const; + + QWidget *createLabel(int level, QWidget *parent) override; + QWidget *createEditor(QWidget *parent) override; + +signals: + void placeholderTextChanged(const QString &text); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + QIcon m_plainTypeIcon; + QString m_placeholderText; + QVariant m_value; + bool m_hasFocus = false; + const ClassPropertyType *m_parentClassType = nullptr; +}; + +inline void AddValueProperty::setParentClassType(const ClassPropertyType *parentClassType) +{ + m_parentClassType = parentClassType; +} + +inline QVariant AddValueProperty::value() const +{ + return m_value; +} + } // namespace Tiled From b6e75103d2df40b5323c8d02b763882dd40a7f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 15 Nov 2024 16:34:21 +0100 Subject: [PATCH 67/78] Fixed updating of layer names in Properties view When changed by other means (scripting, undo/redo, Layers view), the name wasn't updating due to a missing valueChanged call. --- src/tiled/propertieswidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 0cd8f74c09..5a374b6e8e 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -1068,6 +1068,8 @@ class LayerProperties : public ObjectProperties if (layerChange.layer != layer()) return; + if (layerChange.properties & LayerChangeEvent::NameProperty) + emit mNameProperty->valueChanged(); if (layerChange.properties & LayerChangeEvent::VisibleProperty) emit mVisibleProperty->valueChanged(); if (layerChange.properties & LayerChangeEvent::LockedProperty) From 938058782eeb460d7818700dd7c11b731062778a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 15 Nov 2024 16:42:33 +0100 Subject: [PATCH 68/78] Added clear button to the editor for color properties --- src/tiled/varianteditor.cpp | 40 +++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 1af8dc8002..40fa66eecb 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -430,22 +430,46 @@ QWidget *RectFProperty::createEditor(QWidget *parent) return editor; } -// todo: needs to allow setting color back to invalid (unset) QWidget *ColorProperty::createEditor(QWidget *parent) { - auto editor = new ColorButton(parent); - editor->setShowAlphaChannel(m_alpha); + auto editor = new QWidget(parent); + auto layout = new QHBoxLayout(editor); + layout->setContentsMargins(QMargins()); + layout->setSpacing(0); + + auto colorButton = new ColorButton(editor); + colorButton->setShowAlphaChannel(m_alpha); + colorButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + + QIcon resetIcon(QStringLiteral(":/images/16/edit-clear.png")); + resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + + auto resetButton = new QToolButton(editor); + resetButton->setIcon(resetIcon); + resetButton->setToolTip(tr("Clear Color")); + Utils::setThemeIcon(resetButton, "edit-clear"); + + layout->addWidget(colorButton); + layout->addWidget(resetButton); auto syncEditor = [=] { - const QSignalBlocker blocker(editor); - editor->setColor(value()); + const QSignalBlocker blocker(colorButton); + auto v = value(); + colorButton->setColor(v); + resetButton->setEnabled(v.isValid()); }; syncEditor(); - connect(this, &Property::valueChanged, editor, syncEditor); - connect(editor, &ColorButton::colorChanged, this, [this, editor] { - setValue(editor->color()); + connect(resetButton, &QToolButton::clicked, colorButton, [colorButton] { + colorButton->setColor(QColor()); }); + connect(this, &Property::valueChanged, colorButton, syncEditor); + connect(colorButton, &ColorButton::colorChanged, this, [=] { + resetButton->setEnabled(colorButton->color().isValid()); + setValue(colorButton->color()); + }); + + editor->setFocusProxy(colorButton); return editor; } From d1ddc01bb2242a33900e4d0fc48b6f42314edd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 21 Nov 2024 13:14:07 +0100 Subject: [PATCH 69/78] Use VariantEditor directly for ChildrenOnly properties We don't really need to put it in a QWidget with a vertical layout. --- src/tiled/varianteditor.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 40fa66eecb..50c957eb25 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -759,19 +759,27 @@ QWidget *VariantEditor::createPropertyWidget(Property *property) removeProperty(static_cast(object)); }); - const auto halfSpacing = Utils::dpiScaled(2); - if (displayMode == Property::DisplayMode::ChildrenOnly) { + auto editor = new VariantEditor(this); + editor->setEnabled(property->isEnabled()); + + connect(property, &Property::enabledChanged, + editor, &QWidget::setEnabled); + if (auto groupProperty = qobject_cast(property)) { - auto containerWidget = new QWidget(this); - widgets.childrenLayout = new QVBoxLayout(containerWidget); - widgets.childrenLayout->setContentsMargins(0, 0, 0, 0); - setPropertyChildrenExpanded(groupProperty, true); - widgets.rowWidget = containerWidget; - return widgets.rowWidget; + for (auto property : groupProperty->subProperties()) + editor->addProperty(property); + + connect(groupProperty, &GroupProperty::propertyAdded, + editor, &VariantEditor::insertProperty); } + + widgets.rowWidget = editor; + return widgets.rowWidget; } + const auto halfSpacing = Utils::dpiScaled(2); + auto rowWidget = new QWidget(this); auto rowLayout = new QHBoxLayout(rowWidget); rowLayout->setSpacing(halfSpacing * 2); From 9b0040267600450bb30efde20dcae3911573ac1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 21 Nov 2024 17:01:51 +0100 Subject: [PATCH 70/78] Disable custom property editing for tileset objects in map editor --- src/tiled/propertieswidget.cpp | 8 ++++++-- src/tiled/variantmapproperty.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 5a374b6e8e..8ea9597a15 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2231,7 +2231,6 @@ void PropertiesWidget::setDocument(Document *document) mDocument->disconnect(this); mDocument = document; - // mPropertyBrowser->setDocument(document); mCustomProperties->setDocument(document); if (document) { @@ -2262,7 +2261,8 @@ GroupProperty *PropertiesWidget::customPropertiesGroup() const void PropertiesWidget::selectCustomProperty(const QString &name) { - // mPropertyBrowser->selectCustomProperty(name); + if (auto property = mCustomProperties->property(name)) + mPropertyBrowser->focusProperty(property); } void PropertiesWidget::currentObjectChanged(Object *object) @@ -2409,6 +2409,10 @@ void CustomProperties::refresh() mDocument->currentObject()->inheritedProperties()); setValue(currentObjectProperties, suggestedProperties); + + const bool editingTileset = mDocument->type() == Document::TilesetDocumentType; + const bool partOfTileset = mDocument->currentObject()->isPartOfTileset(); + setEnabled(!partOfTileset || editingTileset); } void CustomProperties::setPropertyValue(const QStringList &path, const QVariant &value) diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index 41a41aa39b..076b85d4a3 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -440,7 +440,7 @@ void VariantMapProperty::memberContextMenuRequested(Property *property, const QS } // Provide the Add, Remove and Reset actions also here - if (property->actions()) { + if (isEnabled() && property->actions()) { menu.addSeparator(); if (property->actions() & Property::Action::Add) { From eb3eb104c1b55b89bbe5c6f27d18e4bb0f5519da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 21 Nov 2024 17:27:28 +0100 Subject: [PATCH 71/78] Made custom properties selectable - Top-level properties can be selected by clicking. - Holding Ctrl or Shift allows selecting multiple. - Keyboard handling for selection implemented. - Remove and Rename actions on tool bar now functional. - Copy/paste shortcuts work. As part of implementing this, the VariantEditor and VariantEditorView have been merged into a single PropertiesView class. ToDo: - selectedPropertiesChanged may not get emitted when a selected property is removed. - Add Copy/Paste actions to context menu. --- src/tiled/projectpropertiesdialog.cpp | 4 +- src/tiled/propertieswidget.cpp | 105 ++- src/tiled/propertieswidget.h | 10 +- src/tiled/propertyeditorwidgets.cpp | 146 +++- src/tiled/propertyeditorwidgets.h | 40 +- src/tiled/propertytypeseditor.cpp | 75 +- src/tiled/propertytypeseditor.h | 7 +- src/tiled/varianteditor.cpp | 971 +++++++++++++++++--------- src/tiled/varianteditor.h | 95 +-- src/tiled/variantmapproperty.cpp | 4 +- src/tiled/variantmapproperty.h | 8 +- 11 files changed, 956 insertions(+), 509 deletions(-) diff --git a/src/tiled/projectpropertiesdialog.cpp b/src/tiled/projectpropertiesdialog.cpp index ec2ae91e71..09823b937b 100644 --- a/src/tiled/projectpropertiesdialog.cpp +++ b/src/tiled/projectpropertiesdialog.cpp @@ -104,8 +104,8 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project &project, QWidget *pare ui->propertiesWidget->customPropertiesGroup()->setName(QString()); // Tweak margins - const auto margin = Utils::dpiScaled(3); - ui->propertiesWidget->propertiesView()->widget()->setContentsMargins(0, margin, 0, margin); + const auto halfSpacing = Utils::dpiScaled(2); + ui->propertiesWidget->propertiesView()->widget()->setContentsMargins(0, halfSpacing, 0, halfSpacing); ui->propertiesWidget->setDocument(mLocalProjectDocument); } diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 8ea9597a15..d7d647b8c6 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2160,9 +2160,12 @@ class WangColorProperties : public ObjectProperties PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} - , mCustomProperties(new CustomProperties(this)) - , mPropertyBrowser(new VariantEditorView(this)) + , mRootProperty(new GroupProperty()) + , mCustomProperties(new CustomProperties(mRootProperty)) + , mPropertiesView(new PropertiesView(this)) { + mRootProperty->addProperty(mCustomProperties); + mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); @@ -2181,8 +2184,8 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mActionRenameProperty->setEnabled(false); mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); mActionRenameProperty->setPriority(QAction::LowPriority); - // connect(mActionRenameProperty, &QAction::triggered, - // this, &PropertiesWidget::renameProperty); + connect(mActionRenameProperty, &QAction::triggered, + this, &PropertiesWidget::renameSelectedProperty); Utils::setThemeIcon(mActionAddProperty, "add"); Utils::setThemeIcon(mActionRemoveProperty, "remove"); @@ -2200,15 +2203,15 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - layout->addWidget(mPropertyBrowser); + layout->addWidget(mPropertiesView); layout->addWidget(toolBar); setLayout(layout); - mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); - connect(mPropertyBrowser, &QWidget::customContextMenuRequested, + mPropertiesView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(mPropertiesView, &QWidget::customContextMenuRequested, this, &PropertiesWidget::showContextMenu); - // connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, - // this, &PropertiesWidget::updateActions); + connect(mPropertiesView, &PropertiesView::selectedPropertiesChanged, + this, &PropertiesWidget::updateActions); connect(mCustomProperties, &VariantMapProperty::renameRequested, this, &PropertiesWidget::renameProperty); @@ -2219,7 +2222,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) PropertiesWidget::~PropertiesWidget() { // Disconnect to avoid crashing due to signals emitted during destruction - mPropertyBrowser->disconnect(this); + mPropertiesView->disconnect(this); } void PropertiesWidget::setDocument(Document *document) @@ -2261,25 +2264,25 @@ GroupProperty *PropertiesWidget::customPropertiesGroup() const void PropertiesWidget::selectCustomProperty(const QString &name) { - if (auto property = mCustomProperties->property(name)) - mPropertyBrowser->focusProperty(property); + if (auto property = mCustomProperties->property(name)) { + mPropertiesView->focusProperty(property); + mPropertiesView->setSelectedProperties({ property }); + } } void PropertiesWidget::currentObjectChanged(Object *object) { - mPropertyBrowser->clear(); - - // Remember the expanded states if (mPropertiesObject) { + // Remember the expanded states 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; + mRootProperty->deleteProperty(mPropertiesObject); + mPropertiesObject = nullptr; + } if (object) { switch (object->typeId()) { @@ -2347,20 +2350,17 @@ void PropertiesWidget::currentObjectChanged(Object *object) if (auto subGroupProperty = qobject_cast(subProperties.at(i))) subGroupProperty->setExpanded(mExpandedStates.value(i, true)); } - } - if (object) { - if (mPropertiesObject) - mPropertyBrowser->addProperty(mPropertiesObject); - - mPropertyBrowser->addProperty(mCustomProperties); + mRootProperty->insertProperty(0, mPropertiesObject); } + mPropertiesView->setRootProperty(object ? mRootProperty : nullptr); + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; bool isTileset = object && object->isPartOfTileset(); bool enabled = object && (!isTileset || editingTileset); - mPropertyBrowser->setEnabled(object); + mPropertiesView->setEnabled(object); mActionAddProperty->setEnabled(enabled); } @@ -2430,18 +2430,16 @@ void CustomProperties::setPropertyValue(const QStringList &path, const QVariant void PropertiesWidget::updateActions() { -#if 0 - const QList items = mPropertyBrowser->selectedItems(); - bool allCustomProperties = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); + const auto properties = mPropertiesView->selectedProperties(); bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; - bool isTileset = mPropertyBrowser->object() && mPropertyBrowser->object()->isPartOfTileset(); - bool canModify = allCustomProperties && (!isTileset || editingTileset); + bool isTileset = mDocument && mDocument->currentObject() && mDocument->currentObject()->isPartOfTileset(); + bool canModify = !properties.isEmpty() && (!isTileset || editingTileset); // Disable remove and rename actions when none of the selected objects // actually have the selected property (it may be inherited). if (canModify) { - for (QtBrowserItem *item : items) { - if (!anyObjectHasProperty(mDocument->currentObjects(), item->property()->propertyName())) { + for (auto property : properties) { + if (!anyObjectHasProperty(mDocument->currentObjects(), property->name())) { canModify = false; break; } @@ -2449,8 +2447,7 @@ void PropertiesWidget::updateActions() } mActionRemoveProperty->setEnabled(canModify); - mActionRenameProperty->setEnabled(canModify && items.size() == 1); -#endif + mActionRenameProperty->setEnabled(canModify && properties.size() == 1); } void PropertiesWidget::cutProperties() @@ -2461,19 +2458,15 @@ void PropertiesWidget::cutProperties() bool PropertiesWidget::copyProperties() { -#if 0 - Object *object = mPropertyBrowser->object(); + Object *object = mDocument ? mDocument->currentObject() : nullptr; if (!object) return false; Properties properties; - const QList items = mPropertyBrowser->selectedItems(); - for (QtBrowserItem *item : items) { - if (!mPropertyBrowser->isCustomPropertyItem(item)) - return false; - - const QString name = item->property()->propertyName(); + const auto selectedProperties = mPropertiesView->selectedProperties(); + for (auto property : selectedProperties) { + const QString name = property->name(); const QVariant value = object->property(name); if (!value.isValid()) return false; @@ -2482,7 +2475,7 @@ bool PropertiesWidget::copyProperties() } ClipboardManager::instance()->setProperties(properties); -#endif + return true; } @@ -2540,7 +2533,7 @@ void PropertiesWidget::showAddValueProperty() mCustomProperties->addProperty(mAddValueProperty); } - mPropertyBrowser->focusProperty(mAddValueProperty, VariantEditor::FocusLabel); + mPropertiesView->focusProperty(mAddValueProperty, PropertiesView::FocusLabel); } void PropertiesWidget::addProperty(const QString &name, const QVariant &value) @@ -2558,24 +2551,20 @@ void PropertiesWidget::addProperty(const QString &name, const QVariant &value) name, value)); } - if (auto property = mCustomProperties->property(name)) - mPropertyBrowser->focusProperty(property); + selectCustomProperty(name); } void PropertiesWidget::removeProperties() { -#if 0 Object *object = mDocument->currentObject(); if (!object) return; - const QList items = mPropertyBrowser->selectedItems(); - if (items.isEmpty() || !mPropertyBrowser->allCustomPropertyItems(items)) - return; + const auto properties = mPropertiesView->selectedProperties(); QStringList propertyNames; - for (QtBrowserItem *item : items) - propertyNames.append(item->property()->propertyName()); + for (auto property : properties) + propertyNames.append(property->name()); QUndoStack *undoStack = mDocument->undoStack(); undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock", @@ -2590,12 +2579,20 @@ void PropertiesWidget::removeProperties() } undoStack->endMacro(); -#endif +} + +void PropertiesWidget::renameSelectedProperty() +{ + const auto properties = mPropertiesView->selectedProperties(); + if (properties.size() != 1) + return; + + renameProperty(properties.first()->name()); } void PropertiesWidget::renameProperty(const QString &name) { - QInputDialog *dialog = new QInputDialog(mPropertyBrowser); + QInputDialog *dialog = new QInputDialog(mPropertiesView); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(QCoreApplication::translate("Tiled::PropertiesDock", "Name:")); diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index 050fa8cf95..ce0dd22e95 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -35,10 +35,10 @@ class CustomProperties; class Document; class GroupProperty; class ObjectProperties; -class VariantEditorView; +class PropertiesView; /** - * The PropertiesWidget combines the PropertyBrowser with some controls that + * The PropertiesWidget combines the PropertiesView with some controls that * allow adding and removing properties. It also implements cut, copy and paste * actions and the context menu. */ @@ -56,7 +56,7 @@ class PropertiesWidget : public QWidget void setDocument(Document *document); GroupProperty *customPropertiesGroup() const; - VariantEditorView *propertiesView() const { return mPropertyBrowser; } + PropertiesView *propertiesView() const { return mPropertiesView; } signals: void bringToFront(); @@ -78,17 +78,19 @@ public slots: void showAddValueProperty(); void addProperty(const QString &name, const QVariant &value); void removeProperties(); + void renameSelectedProperty(); void renameProperty(const QString &name); void showContextMenu(const QPoint &pos); void retranslateUi(); Document *mDocument = nullptr; + GroupProperty *mRootProperty = nullptr; ObjectProperties *mPropertiesObject = nullptr; CustomProperties *mCustomProperties = nullptr; QPointer mAddValueProperty; QMap mExpandedStates; - VariantEditorView *mPropertyBrowser; + PropertiesView *mPropertiesView; QAction *mActionAddProperty; QAction *mActionRemoveProperty; QAction *mActionRenameProperty; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index ff95d55757..b101cd3ca3 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -21,6 +21,7 @@ #include "propertyeditorwidgets.h" #include "utils.h" +#include "varianteditor.h" #include #include @@ -659,6 +660,15 @@ void ElidingLabel::setToolTip(const QString &toolTip) QLabel::setToolTip(m_toolTip); } +void ElidingLabel::setSelected(bool selected) +{ + if (m_selected == selected) + return; + + m_selected = selected; + update(); +} + QSize ElidingLabel::minimumSizeHint() const { auto hint = QLabel::minimumSizeHint(); @@ -688,7 +698,8 @@ void ElidingLabel::paintEvent(QPaintEvent *) } QStylePainter p(this); - p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, foregroundRole()); + QPalette::ColorRole role = m_selected ? QPalette::HighlightedText : foregroundRole(); + p.drawItemText(cr, flags, opt.palette, isEnabled(), elidedText, role); } @@ -748,23 +759,31 @@ void PropertyLabel::setModified(bool modified) bool PropertyLabel::event(QEvent *event) { + switch (event->type()) { // Handled here instead of in mousePressEvent because we want it to be // expandable also when the label is disabled. - if (event->type() == QEvent::MouseButtonPress && m_expandable) { - auto mouseEvent = static_cast(event); - if (mouseEvent->button() == Qt::LeftButton) { - setExpanded(!m_expanded); - return true; - } - } - - if (event->type() == QEvent::ContextMenu) { - emit contextMenuRequested(static_cast(event)->globalPos()); - return true; + case QEvent::MouseButtonPress: { + const auto pos = static_cast(event)->pos(); + if (!isHeader() && !branchIndicatorRect().contains(pos)) + break; } + [[fallthrough]]; + case QEvent::MouseButtonDblClick: + if (m_expandable) { + if (static_cast(event)->button() == Qt::LeftButton) { + setExpanded(!m_expanded); + return true; + } + } + break; - if (event->type() == QEvent::LayoutDirectionChange) + case QEvent::LayoutDirectionChange: updateContentMargins(); + break; + + default: + break; + } return ElidingLabel::event(event); } @@ -773,18 +792,12 @@ void PropertyLabel::paintEvent(QPaintEvent *event) { ElidingLabel::paintEvent(event); - const int spacing = Utils::dpiScaled(3); - const int branchIndicatorWidth = Utils::dpiScaled(14); - const int indent = branchIndicatorWidth * m_level; - QStyleOption branchOption; branchOption.initFrom(this); - if (branchOption.direction == Qt::LeftToRight) - branchOption.rect = QRect(indent, 0, - branchIndicatorWidth + spacing, height()); - else - branchOption.rect = QRect(width() - indent - branchIndicatorWidth - spacing, 0, - branchIndicatorWidth + spacing, height()); + branchOption.rect = branchIndicatorRect(); + + if (isSelected()) + branchOption.state |= QStyle::State_Selected; if (m_expandable) branchOption.state |= QStyle::State_Children; if (m_expanded) @@ -815,6 +828,21 @@ void PropertyLabel::updateContentMargins() setContentsMargins(spacing, verticalSpacing, spacing + indent, verticalSpacing); } +QRect PropertyLabel::branchIndicatorRect() const +{ + const int spacing = Utils::dpiScaled(3); + const int branchIndicatorWidth = Utils::dpiScaled(14); + const int indent = branchIndicatorWidth * m_level; + + if (layoutDirection() == Qt::LeftToRight) { + return QRect(indent, 0, + branchIndicatorWidth + spacing, height()); + } else { + return QRect(width() - indent - branchIndicatorWidth - spacing, 0, + branchIndicatorWidth + spacing, height()); + } +} + /** * To fit better alongside other widgets without vertical centering, the size * hint is adjusted to match that of a QLineEdit. @@ -836,6 +864,78 @@ QSize PropertyLabel::sizeHint() const return style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h), this); } + +PropertyWidget::PropertyWidget(Property *property, QWidget *parent) + : QWidget(parent) + , m_property(property) +{ + setSelected(property->isSelected()); + + connect(property, &Property::selectedChanged, this, &PropertyWidget::setSelected); +} + +void PropertyWidget::setSelectable(bool selectable) +{ + if (m_selectable == selectable) + return; + + m_selectable = selectable; + + if (!selectable) + setSelected(false); +} + +void PropertyWidget::setSelected(bool selected) +{ + if (m_selected == selected) + return; + + m_selected = selected; + update(); +} + +void PropertyWidget::paintEvent(QPaintEvent *event) +{ + QWidget::paintEvent(event); + + const auto halfSpacing = Utils::dpiScaled(2); + const QRect r = rect().adjusted(halfSpacing, 0, -halfSpacing, 0); + QStylePainter painter(this); + + if (isSelected()) + painter.fillRect(r, palette().highlight()); + + if (hasFocus()) { + QStyleOptionFocusRect option; + option.initFrom(this); + option.rect = r; + option.backgroundColor = palette().color(backgroundRole()); + option.state |= QStyle::State_KeyboardFocusChange; + + painter.drawPrimitive(QStyle::PE_FrameFocusRect, option); + } +} + +void PropertyWidget::mousePressEvent(QMouseEvent *event) +{ + // Not 100% correct hack to make sure only one property is selected when + // the context menu opens. Handling of the context menu should probably be + // moved elsewhere. + if (event->button() == Qt::RightButton) + emit clicked(Qt::NoModifier); + else + emit clicked(event->modifiers()); + + setFocus(Qt::MouseFocusReason); + + QWidget::mousePressEvent(event); +} + +void PropertyWidget::contextMenuEvent(QContextMenuEvent *event) +{ + emit m_property->contextMenuRequested(event->globalPos()); +} + } // namespace Tiled #include "moc_propertyeditorwidgets.cpp" diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index 63721a98b6..c08b86cc7e 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -29,6 +29,8 @@ class QLabel; namespace Tiled { +class Property; + /** * A slider that doesn't respond to wheel events when not focused. */ @@ -291,6 +293,9 @@ class ElidingLabel : public QLabel void setToolTip(const QString &toolTip); + void setSelected(bool selected); + bool isSelected() const { return m_selected; } + QSize minimumSizeHint() const override; protected: @@ -299,6 +304,7 @@ class ElidingLabel : public QLabel private: QString m_toolTip; bool m_isElided = false; + bool m_selected = false; }; /** @@ -328,7 +334,6 @@ class PropertyLabel : public ElidingLabel signals: void toggled(bool expanded); - void contextMenuRequested(const QPoint &globalPos); protected: bool event(QEvent *event) override; @@ -336,6 +341,7 @@ class PropertyLabel : public ElidingLabel private: void updateContentMargins(); + QRect branchIndicatorRect() const; int m_level = 0; bool m_header = false; @@ -343,4 +349,36 @@ class PropertyLabel : public ElidingLabel bool m_expanded = false; }; +/** + * A widget that represents a single property. + */ +class PropertyWidget : public QWidget +{ + Q_OBJECT + +public: + PropertyWidget(Property *property, QWidget *parent = nullptr); + + Property *property() const { return m_property; } + + void setSelectable(bool selectable); + bool isSelectable() const { return m_selectable; } + + void setSelected(bool selected); + bool isSelected() const { return m_selected; } + +signals: + void clicked(Qt::KeyboardModifiers modifiers); + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; + +private: + Property *m_property; + bool m_selectable = false; + bool m_selected = false; +}; + } // namespace Tiled diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 53fe0bde95..7e82cf4264 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -221,8 +221,8 @@ PropertyTypesEditor::PropertyTypesEditor(QWidget *parent) this, &PropertyTypesEditor::openAddMemberDialog); connect(mRemoveMemberAction, &QAction::triggered, this, &PropertyTypesEditor::removeMember); - // connect(mRenameMemberAction, &QAction::triggered, - // this, &PropertyTypesEditor::renameMember); + connect(mRenameMemberAction, &QAction::triggered, + this, &PropertyTypesEditor::renameSelectedMember); connect(mPropertyTypesModel, &PropertyTypesModel::nameChanged, this, &PropertyTypesEditor::propertyTypeNameChanged); @@ -367,11 +367,12 @@ ClassPropertyType *PropertyTypesEditor::selectedClassPropertyType() const return static_cast(propertyType); } -void PropertyTypesEditor::currentMemberItemChanged(QtBrowserItem *item) +void PropertyTypesEditor::selectedMembersChanged() { - // todo: change based on selection in VariantEditor - mRemoveMemberAction->setEnabled(item); - mRenameMemberAction->setEnabled(item); + const auto properties = mMembersView->selectedProperties(); + const bool singlePropertySelected = properties.size() == 1; + mRemoveMemberAction->setEnabled(singlePropertySelected); + mRenameMemberAction->setEnabled(singlePropertySelected); } void PropertyTypesEditor::propertyTypeNameChanged(const QModelIndex &index, const PropertyType &type) @@ -582,7 +583,7 @@ void PropertyTypesEditor::openAddMemberDialog() mMembersProperty->addProperty(mAddValueProperty); } - mMembersEditor->focusProperty(mAddValueProperty, VariantEditor::FocusLabel); + mMembersView->focusProperty(mAddValueProperty, PropertiesView::FocusLabel); } void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) @@ -605,23 +606,23 @@ void PropertyTypesEditor::addMember(const QString &name, const QVariant &value) applyMemberToSelectedType(name, value); updateDetails(); - if (auto property = mMembersProperty->property(name)) - mMembersEditor->focusProperty(property); + if (auto property = mMembersProperty->property(name)) { + mMembersView->focusProperty(property); + mMembersView->setSelectedProperties({ property }); + } } void PropertyTypesEditor::removeMember() { - // todo: VariantEditor needs to support selection -#if 0 - QtBrowserItem *item = mMembersView->currentItem(); - if (!item) - return; - PropertyType *propertyType = selectedPropertyType(); if (!propertyType || !propertyType->isClass()) return; - const QString name = item->property()->propertyName(); + const auto properties = mMembersView->selectedProperties(); + if (properties.size() != 1) + return; + + const QString name = properties.first()->name(); if (!confirm(tr("Remove Member"), tr("Are you sure you want to remove '%1' from class '%2'? This action cannot be undone.") @@ -630,26 +631,30 @@ void PropertyTypesEditor::removeMember() } // Select a different item before removing the current one - QList items = mMembersView->topLevelItems(); - if (items.count() > 1) { - const int currentItemIndex = items.indexOf(item); - if (item == items.last()) - mMembersView->setCurrentItem(items.at(currentItemIndex - 1)); + const auto &subProperties = mMembersProperty->subProperties(); + if (subProperties.size() > 1) { + const int currentIndex = subProperties.indexOf(properties.first()); + if (currentIndex == subProperties.size() - 1) + subProperties[currentIndex - 1]->setSelected(true); else - mMembersView->setCurrentItem(items.at(currentItemIndex + 1)); + subProperties[currentIndex + 1]->setSelected(true); } - mPropertiesHelper->deleteProperty(item->property()); + mMembersProperty->removeMember(name); +} - static_cast(*propertyType).members.remove(name); +void PropertyTypesEditor::renameSelectedMember() +{ + const auto properties = mMembersView->selectedProperties(); + if (properties.size() != 1) + return; - applyPropertyTypes(); -#endif + renameMember(properties.first()->name()); } void PropertyTypesEditor::renameMember(const QString &name) { - QInputDialog *dialog = new QInputDialog(mMembersEditor); + QInputDialog *dialog = new QInputDialog(mMembersView); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(tr("Name:")); @@ -895,7 +900,7 @@ void PropertyTypesEditor::setCurrentPropertyType(PropertyType::Type type) mStorageTypeComboBox = nullptr; mValuesAsFlagsCheckBox = nullptr; mValuesView = nullptr; - mMembersEditor = nullptr; + mMembersView = nullptr; mAddValueAction->setEnabled(type == PropertyType::PT_Enum); mAddMemberAction->setEnabled(type == PropertyType::PT_Class); @@ -938,21 +943,21 @@ void PropertyTypesEditor::addClassProperties() nameAndColor->addWidget(mColorButton); nameAndColor->addWidget(mDrawFillCheckBox); - mMembersEditor = new VariantEditorView(this); + mMembersView = new PropertiesView(this); - const auto margin = Utils::dpiScaled(3); - mMembersEditor->widget()->setContentsMargins(0, margin, 0, margin); + const auto halfSpacing = Utils::dpiScaled(2); + mMembersView->widget()->setContentsMargins(0, halfSpacing, 0, halfSpacing); mMembersProperty = new VariantMapProperty(QString(), this); - mMembersEditor->addProperty(mMembersProperty); + mMembersView->setRootProperty(mMembersProperty); connect(mMembersProperty, &VariantMapProperty::valueChanged, this, &PropertyTypesEditor::classMembersChanged); connect(mMembersProperty, &VariantMapProperty::renameRequested, this, &PropertyTypesEditor::renameMember); - // connect(mMembersView, &QtTreePropertyBrowser::currentItemChanged, - // this, &PropertyTypesEditor::currentMemberItemChanged); + connect(mMembersView, &PropertiesView::selectedPropertiesChanged, + this, &PropertyTypesEditor::selectedMembersChanged); mUseAsPropertyCheckBox = new QCheckBox(tr("Property value")); @@ -982,7 +987,7 @@ void PropertyTypesEditor::addClassProperties() auto membersWithToolBarLayout = new QVBoxLayout; membersWithToolBarLayout->setSpacing(0); membersWithToolBarLayout->setContentsMargins(0, 0, 0, 0); - membersWithToolBarLayout->addWidget(mMembersEditor); + membersWithToolBarLayout->addWidget(mMembersView); membersWithToolBarLayout->addWidget(membersToolBar); mDetailsLayout->addRow(tr("Name"), nameAndColor); diff --git a/src/tiled/propertytypeseditor.h b/src/tiled/propertytypeseditor.h index afd4041eda..b4db664506 100644 --- a/src/tiled/propertytypeseditor.h +++ b/src/tiled/propertytypeseditor.h @@ -43,8 +43,8 @@ namespace Tiled { class AddValueProperty; class ColorButton; +class PropertiesView; class PropertyTypesModel; -class VariantEditorView; class VariantMapProperty; struct PropertyTypesFilter @@ -81,7 +81,7 @@ class PropertyTypesEditor : public QDialog PropertyType *selectedPropertyType() const; ClassPropertyType *selectedClassPropertyType() const; - void currentMemberItemChanged(QtBrowserItem *item); + void selectedMembersChanged(); void propertyTypeNameChanged(const QModelIndex &index, const PropertyType &type); @@ -107,6 +107,7 @@ class PropertyTypesEditor : public QDialog void openAddMemberDialog(); void addMember(const QString &name, const QVariant &value = QVariant()); void removeMember(); + void renameSelectedMember(); void renameMember(const QString &name); void renameMemberTo(const QString &oldName, const QString &name); @@ -145,7 +146,7 @@ class PropertyTypesEditor : public QDialog QCheckBox *mDrawFillCheckBox = nullptr; QCheckBox *mClassOfCheckBox = nullptr; QPushButton *mClassOfButton = nullptr; - VariantEditorView *mMembersEditor = nullptr; + PropertiesView *mMembersView = nullptr; VariantMapProperty *mMembersProperty = nullptr; QPointer mAddValueProperty; diff --git a/src/tiled/varianteditor.cpp b/src/tiled/varianteditor.cpp index 50c957eb25..97e5ca223f 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/varianteditor.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,14 @@ void Property::setModified(bool modified) } } +void Property::setSelected(bool selected) +{ + if (m_selected != selected) { + m_selected = selected; + emit selectedChanged(selected); + } +} + void Property::setActions(Actions actions) { if (m_actions != actions) { @@ -87,16 +96,15 @@ QWidget *Property::createLabel(int level, QWidget *parent) auto label = new PropertyLabel(parent); label->setLevel(level); - connect(label, &PropertyLabel::contextMenuRequested, - this, &Property::contextMenuRequested); - if (displayMode() != Property::DisplayMode::NoLabel) { label->setText(name()); - label->setModified(isModified()); label->setToolTip(toolTip()); + label->setModified(isModified()); + label->setSelected(isSelected()); connect(this, &Property::nameChanged, label, &PropertyLabel::setText); connect(this, &Property::toolTipChanged, label, &PropertyLabel::setToolTip); connect(this, &Property::modifiedChanged, label, &PropertyLabel::setModified); + connect(this, &Property::selectedChanged, label, &PropertyLabel::setSelected); } if (displayMode() == Property::DisplayMode::Header) @@ -616,325 +624,6 @@ QWidget *QtAlignmentProperty::createEditor(QWidget *parent) return editor; } - -VariantEditor::VariantEditor(QWidget *parent) - : QWidget(parent) - , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) - , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) - , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) -{ - m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); - m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); - m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); - - m_layout = new QVBoxLayout(this); - - m_layout->setContentsMargins(QMargins()); - m_layout->setSpacing(0); -} - -/** - * Removes all properties from the editor. The properties are not deleted. - */ -void VariantEditor::clear() -{ - QHashIterator it(m_propertyWidgets); - while (it.hasNext()) { - it.next(); - delete it.value().rowWidget; - - it.key()->disconnect(this); - } - m_propertyWidgets.clear(); -} - -/** - * Adds the given property. - * Does not take ownership of the property. - */ -void VariantEditor::addProperty(Property *property) -{ - m_layout->addWidget(createPropertyWidget(property)); -} - -/** - * Insert the given property at the given index. - * Does not take ownership of the property. - */ -void VariantEditor::insertProperty(int index, Property *property) -{ - m_layout->insertWidget(index, createPropertyWidget(property)); -} - -/** - * Removes the given property from the editor. The property is not deleted. - */ -void VariantEditor::removeProperty(Property *property) -{ - auto it = m_propertyWidgets.constFind(property); - Q_ASSERT(it != m_propertyWidgets.constEnd()); - - if (it == m_propertyWidgets.constEnd()) - return; - - // Immediately remove from layout, but delete later to avoid deleting - // widgets while they are still handling events. - auto rowWidget = it.value().rowWidget; - m_layout->removeWidget(rowWidget); - rowWidget->deleteLater(); - - m_propertyWidgets.erase(it); - - // This appears to be necessary to avoid flickering due to relayouting - // not being done before the next paint. - QWidget *widget = this; - while (widget && widget->layout()) { - widget->layout()->activate(); - widget = widget->parentWidget(); - } - - property->disconnect(this); -} - -/** - * Focuses the editor or label for the given property. Makes sure any parent - * group properties are expanded. - * - * When the given property is a group property, the group property is expanded - * and the first child property is focused. - * - * Returns the focused widget or nullptr if the property was not found. - */ -QWidget *VariantEditor::focusProperty(Property *property, FocusTarget target) -{ - for (auto it = m_propertyWidgets.constBegin(); it != m_propertyWidgets.constEnd(); ++it) { - auto &widgets = it.value(); - - if (it.key() == property) { - if (target == FocusEditor && widgets.editor) { - widgets.editor->setFocus(); - return widgets.editor; - } - if (target == FocusLabel && widgets.label) { - widgets.label->setFocus(); - return widgets.label; - } - if (auto groupProperty = qobject_cast(it.key())) { - groupProperty->setExpanded(true); - if (widgets.children && !groupProperty->subProperties().isEmpty()) - widgets.children->focusProperty(groupProperty->subProperties().first(), target); - return widgets.children; - } - return nullptr; - } else if (auto groupProperty = qobject_cast(it.key())) { - if (widgets.children) { - if (auto w = widgets.children->focusProperty(property, target)) { - groupProperty->setExpanded(true); - return w; - } - } - } - } - - return nullptr; -} - -void VariantEditor::setLevel(int level) -{ - m_level = level; - - setBackgroundRole((m_level % 2) ? QPalette::Base - : QPalette::AlternateBase); - setAutoFillBackground(m_level > 0); -} - -QWidget *VariantEditor::createPropertyWidget(Property *property) -{ - Q_ASSERT(!m_propertyWidgets.contains(property)); - - auto &widgets = m_propertyWidgets[property]; - const auto displayMode = property->displayMode(); - - connect(property, &Property::destroyed, this, [this](QObject *object) { - removeProperty(static_cast(object)); - }); - - if (displayMode == Property::DisplayMode::ChildrenOnly) { - auto editor = new VariantEditor(this); - editor->setEnabled(property->isEnabled()); - - connect(property, &Property::enabledChanged, - editor, &QWidget::setEnabled); - - if (auto groupProperty = qobject_cast(property)) { - for (auto property : groupProperty->subProperties()) - editor->addProperty(property); - - connect(groupProperty, &GroupProperty::propertyAdded, - editor, &VariantEditor::insertProperty); - } - - widgets.rowWidget = editor; - return widgets.rowWidget; - } - - const auto halfSpacing = Utils::dpiScaled(2); - - auto rowWidget = new QWidget(this); - auto rowLayout = new QHBoxLayout(rowWidget); - rowLayout->setSpacing(halfSpacing * 2); - rowLayout->setContentsMargins(0, 0, 0, 0); - - widgets.rowWidget = rowWidget; - - if (displayMode == Property::DisplayMode::Separator) { - auto separator = new QFrame(rowWidget); - rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); - separator->setFrameShape(QFrame::HLine); - separator->setFrameShadow(QFrame::Plain); - separator->setForegroundRole(QPalette::Mid); - rowLayout->addWidget(separator); - return widgets.rowWidget; - } - - widgets.label = property->createLabel(m_level, rowWidget); - - if (displayMode != Property::DisplayMode::Header) { - if (isLeftToRight()) - rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); - else - rowLayout->setContentsMargins(halfSpacing * 2, halfSpacing, 0, halfSpacing); - } - - if (widgets.label) - rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); - - auto editorLayout = new QHBoxLayout; - widgets.editor = property->createEditor(rowWidget); - - if (widgets.editor) { - widgets.editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); - rowLayout->addLayout(editorLayout, EditorStretch); - } else { - rowLayout->addLayout(editorLayout, 0); - } - - widgets.resetButton = new QToolButton(rowWidget); - widgets.resetButton->setToolTip(tr("Reset")); - widgets.resetButton->setIcon(m_resetIcon); - widgets.resetButton->setAutoRaise(true); - widgets.resetButton->setEnabled(property->isModified()); - Utils::setThemeIcon(widgets.resetButton, "edit-clear"); - editorLayout->addWidget(widgets.resetButton, 0, Qt::AlignTop); - connect(widgets.resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); - connect(property, &Property::modifiedChanged, widgets.resetButton, &QWidget::setEnabled); - - widgets.removeButton = new QToolButton(rowWidget); - widgets.removeButton->setToolTip(tr("Remove")); - widgets.removeButton->setIcon(m_removeIcon); - widgets.removeButton->setAutoRaise(true); - Utils::setThemeIcon(widgets.removeButton, "remove"); - editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); - connect(widgets.removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); - - widgets.addButton = new QToolButton(rowWidget); - widgets.addButton->setToolTip(tr("Add")); - widgets.addButton->setIcon(m_addIcon); - widgets.addButton->setAutoRaise(true); - widgets.addButton->setFocusPolicy(Qt::StrongFocus); // needed for AddValueProperty - Utils::setThemeIcon(widgets.addButton, "add"); - editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); - connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); - - if (auto groupProperty = qobject_cast(property)) { - auto containerWidget = new QWidget(this); - widgets.childrenLayout = new QVBoxLayout(containerWidget); - widgets.childrenLayout->setContentsMargins(0, 0, 0, 0); - widgets.childrenLayout->setSpacing(0); - widgets.childrenLayout->addWidget(rowWidget); - - connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { - setPropertyChildrenExpanded(groupProperty, expanded); - }); - - setPropertyChildrenExpanded(groupProperty, groupProperty->isExpanded()); - - widgets.rowWidget = containerWidget; - } - - updatePropertyEnabled(widgets, property->isEnabled()); - updatePropertyActions(widgets, property->actions()); - - connect(property, &Property::enabledChanged, this, [=] (bool enabled) { - updatePropertyEnabled(m_propertyWidgets[property], enabled); - }); - connect(property, &Property::actionsChanged, this, [=] (Property::Actions actions) { - updatePropertyActions(m_propertyWidgets[property], actions); - }); - - return widgets.rowWidget; -} - -void VariantEditor::setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded) -{ - auto &widgets = m_propertyWidgets[groupProperty]; - - // Create the children editor on-demand - if (expanded && !widgets.children) { - const auto halfSpacing = Utils::dpiScaled(2); - const auto displayMode = groupProperty->displayMode(); - - widgets.children = new VariantEditor(widgets.childrenLayout->parentWidget()); - if (widgets.label && displayMode == Property::DisplayMode::Header) - widgets.children->setContentsMargins(0, halfSpacing, 0, halfSpacing); - if (displayMode == Property::DisplayMode::Default) - widgets.children->setLevel(m_level + 1); - widgets.children->setEnabled(groupProperty->isEnabled()); - for (auto property : groupProperty->subProperties()) - widgets.children->addProperty(property); - - connect(groupProperty, &GroupProperty::propertyAdded, - widgets.children, &VariantEditor::insertProperty); - - widgets.childrenLayout->addWidget(widgets.children); - } - - if (widgets.children) { - widgets.children->setVisible(expanded); - - // needed to avoid flickering when hiding the editor - if (!expanded) { - QWidget *widget = widgets.childrenLayout->parentWidget(); - while (widget && widget->layout()) { - widget->layout()->activate(); - widget = widget->parentWidget(); - } - } - } -} - -void VariantEditor::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) -{ - if (widgets.label) - widgets.label->setEnabled(enabled); - if (widgets.editor) - widgets.editor->setEnabled(enabled); - if (widgets.children) - widgets.children->setEnabled(enabled); -} - -void VariantEditor::updatePropertyActions(const PropertyWidgets &widgets, - Property::Actions actions) -{ - widgets.resetButton->setVisible(actions.testFlag(Property::Action::Reset)); - widgets.removeButton->setVisible(actions.testFlag(Property::Action::Remove)); - widgets.addButton->setVisible(actions.testFlag(Property::Action::Add)); - - widgets.addButton->setEnabled(!actions.testFlag(Property::Action::AddDisabled)); -} - - QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent) { auto editor = new ComboBox(parent); @@ -1095,44 +784,200 @@ Property *createVariantProperty(const QString &name, return nullptr; } -VariantEditorView::VariantEditorView(QWidget *parent) + +PropertiesView::PropertiesView(QWidget *parent) : QScrollArea(parent) + , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) + , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) + , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) + , m_rootLayout(new QVBoxLayout) { - auto scrollWidget = new QWidget(this); - scrollWidget->setBackgroundRole(QPalette::AlternateBase); - scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); + m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); + m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); + + auto scrollWidget = new QWidget(this); + scrollWidget->setBackgroundRole(QPalette::AlternateBase); + scrollWidget->setMinimumWidth(Utils::dpiScaled(120)); auto verticalLayout = new QVBoxLayout(scrollWidget); - m_editor = new VariantEditor(scrollWidget); - verticalLayout->addWidget(m_editor); + verticalLayout->addLayout(m_rootLayout); verticalLayout->addStretch(); verticalLayout->setContentsMargins(QMargins()); + verticalLayout->setSpacing(0); setWidgetResizable(true); setWidget(scrollWidget); } -void VariantEditorView::focusProperty(Property *property, - VariantEditor::FocusTarget target) +/** + * Sets the root property. + * + * The view does not take ownership of the property. + */ +void PropertiesView::setRootProperty(GroupProperty *root) { - if (auto widget = m_editor->focusProperty(property, target)) { - if (widget->isVisible()) { - ensureWidgetVisible(widget); - } else { - // Install event filter to detect when widget becomes visible - widget->installEventFilter(this); + if (m_root == root) + return; + + QHashIterator it(m_propertyWidgets); + while (it.hasNext()) + it.next().key()->disconnect(this); + m_propertyWidgets.clear(); + + Utils::deleteAllFromLayout(m_rootLayout); + + m_root = root; + + if (root) + createPropertyWidgets(root, widget(), 0, m_rootLayout, 0); +} + +static Property *nextProperty(Property *property) +{ + if (auto groupProperty = qobject_cast(property)) + if (groupProperty->isExpanded() && !groupProperty->subProperties().isEmpty()) + return groupProperty->subProperties().first(); + + while (auto parent = property->parentProperty()) { + const int index = parent->indexOfProperty(property); + if (index < parent->subProperties().size() - 1) + return parent->subProperties().at(index + 1); + + property = parent; + } + + return nullptr; +} + +static Property *previousProperty(Property *property) +{ + auto parent = property->parentProperty(); + if (!parent) + return nullptr; + + const int index = parent->indexOfProperty(property); + if (index > 0) { + auto previous = parent->subProperties().at(index - 1); + while (auto groupProperty = qobject_cast(previous)) { + if (groupProperty->isExpanded() && !groupProperty->subProperties().isEmpty()) + previous = groupProperty->subProperties().last(); + else + break; } + return previous; + } + + return parent; +} + +static void collectSelectedProperties(GroupProperty *groupProperty, + QList &selected) +{ + for (auto property : groupProperty->subProperties()) { + if (property->isSelected()) + selected.append(property); + if (auto childGroup = qobject_cast(property)) + collectSelectedProperties(childGroup, selected); } } -bool VariantEditorView::eventFilter(QObject *watched, QEvent *event) +static bool assignSelectedProperties(GroupProperty *groupProperty, + const QList &selected) +{ + bool changed = false; + + for (auto property : groupProperty->subProperties()) { + const bool isSelected = selected.contains(property); + changed |= isSelected != property->isSelected(); + + property->setSelected(isSelected); + + if (auto childGroup = qobject_cast(property)) + changed |= assignSelectedProperties(childGroup, selected); + } + + return changed; +} + +static bool assignSelectedPropertiesRange(GroupProperty *root, + Property *a, + Property *b) +{ + bool changed = false; + bool selected = false; + Property *end = nullptr; + + for (Property *cur = root; cur; cur = nextProperty(cur)) { + if (!end) { + if (cur == a) { + end = b; + selected = true; + } else if (cur == b) { + end = a; + selected = true; + } + } + if (cur->actions().testFlag(Property::Action::Select)) { + changed |= selected != cur->isSelected(); + cur->setSelected(selected); + } + if (cur == end) + selected = false; + } + + return changed; +} + +QList PropertiesView::selectedProperties() const +{ + QList selected; + if (m_root) + collectSelectedProperties(m_root, selected); + return selected; +} + +void PropertiesView::setSelectedProperties(const QList &properties) +{ + if (m_root && assignSelectedProperties(m_root, properties)) + emit selectedPropertiesChanged(); +} + +bool PropertiesView::focusProperty(Property *property, FocusTarget target) +{ + if (!property || !m_root) + return false; + + const auto displayMode = property->displayMode(); + if (displayMode == Property::DisplayMode::Separator || + displayMode == Property::DisplayMode::ChildrenOnly) + return false; + + auto widget = focusPropertyImpl(m_root, property, target); + if (!widget) + return false; + + if (widget->isVisible()) { + const int margin = Utils::dpiScaled(10); + ensureWidgetVisible(widget, margin, margin); + } else { + // Install event filter to detect when widget becomes visible + widget->installEventFilter(this); + } + + return true; +} + +bool PropertiesView::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::Show) { if (QPointer widget = qobject_cast(watched)) { // Schedule after all pending events including layout QMetaObject::invokeMethod(this, [=] { - if (widget) - ensureWidgetVisible(widget); + if (widget) { + const int margin = Utils::dpiScaled(10); + ensureWidgetVisible(widget, margin, margin); + } }, Qt::QueuedConnection); widget->removeEventFilter(this); } @@ -1140,6 +985,452 @@ bool VariantEditorView::eventFilter(QObject *watched, QEvent *event) return QScrollArea::eventFilter(watched, event); } +void PropertiesView::keyPressEvent(QKeyEvent *event) +{ + auto propertyWidget = qobject_cast(focusWidget()); + auto property = propertyWidget ? propertyWidget->property() : nullptr; + const auto key = event->key(); + const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; + + auto focusAndSelect = [=] (Property *property) { + if (focusProperty(property, FocusRow)) + setSelectedProperties({ property }); + }; + + switch (key) { + case Qt::Key_Down: + if (focusNextPrevProperty(property, true, shiftPressed)) + return; + break; + case Qt::Key_Up: + if (focusNextPrevProperty(property, false, shiftPressed)) + return; + break; + + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Plus: + case Qt::Key_Minus: + if (auto groupProperty = qobject_cast(property)) { + const bool expand = + key == Qt::Key_Plus || + key == (isLeftToRight() ? Qt::Key_Right : Qt::Key_Left); + + if (expand) { + if (event->modifiers() & Qt::ControlModifier) { + groupProperty->expandAll(); + } else if (groupProperty->isExpanded() && key != Qt::Key_Plus) { + // If already expanded, focus first child + if (!groupProperty->subProperties().isEmpty()) + focusNextPrevProperty(groupProperty, true, shiftPressed); + } else { + groupProperty->setExpanded(true); + } + } else { + if (event->modifiers() & Qt::ControlModifier) { + groupProperty->collapseAll(); + } else if (!groupProperty->isExpanded() && key != Qt::Key_Minus) { + // If already collapsed, focus parent + focusAndSelect(groupProperty->parentProperty()); + } else { + groupProperty->setExpanded(false); + } + } + } else if (key == Qt::Key_Left || key == Qt::Key_Right) { + const bool focusParent = (key == Qt::Key_Left) ^ isRightToLeft(); + if (focusParent && property) + focusAndSelect(property->parentProperty()); + } + return; + + case Qt::Key_Asterisk: + if (auto groupProperty = qobject_cast(property)) + groupProperty->expandAll(); + return; + } + + QScrollArea::keyPressEvent(event); +} + +bool PropertiesView::focusNextPrevProperty(Property *property, bool next, bool shiftPressed) +{ + if (!property) + return false; + + const auto nextPrev = next ? nextProperty : previousProperty; + + while (Property *propertyToFocus = nextPrev(property)) { + switch (propertyToFocus->displayMode()) { + case Property::DisplayMode::Default: + case Property::DisplayMode::NoLabel: + case Property::DisplayMode::Header: + if (focusProperty(propertyToFocus, FocusRow)) { + if (!shiftPressed) { + m_selectionStart = propertyToFocus; + + if (propertyToFocus->actions().testFlag(Property::Action::Select)) + setSelectedProperties({ propertyToFocus }); + else + setSelectedProperties({}); + } else { + if (assignSelectedPropertiesRange(m_root, m_selectionStart, propertyToFocus)) + emit selectedPropertiesChanged(); + } + + return true; + } + return false; + case Property::DisplayMode::Separator: + case Property::DisplayMode::ChildrenOnly: + break; + } + + property = propertyToFocus; + } + + return false; +} + +/** + * Removes the given property from the editor. The property is not deleted. + */ +void PropertiesView::removeProperty(Property *property) +{ + auto it = m_propertyWidgets.constFind(property); + Q_ASSERT(it != m_propertyWidgets.constEnd()); + + if (it == m_propertyWidgets.constEnd()) + return; + + // Immediately remove from layout, but delete later to avoid deleting + // widgets while they are still handling events. + auto rowWidget = it.value().rowWidget; + + QWidget *widget = rowWidget->parentWidget(); + QLayout *layout = widget->layout(); + layout->removeWidget(rowWidget); + + rowWidget->deleteLater(); + + m_propertyWidgets.erase(it); + + // This appears to be necessary to avoid flickering due to relayouting + // not being done before the next paint. + while (widget && widget->layout()) { + widget->layout()->activate(); + widget = widget->parentWidget(); + } + + property->disconnect(this); +} + +/** + * Focuses the editor or label for the given property. Makes sure any parent + * group properties are expanded. + * + * When the given property is a group property, the group property is expanded + * and the first child property is focused. + * + * Returns the focused widget or nullptr if the property was not found. + */ +QWidget *PropertiesView::focusPropertyImpl(GroupProperty *group, + Property *property, + FocusTarget target) +{ + for (auto subProperty : group->subProperties()) { + auto widgetsIt = m_propertyWidgets.find(subProperty); + if (widgetsIt == m_propertyWidgets.end()) + continue; + + auto &widgets = *widgetsIt; + + if (subProperty == property) { + if (target == PropertiesView::FocusRow) { + widgets.rowWidget->setFocus(); + return widgets.rowWidget->focusWidget(); + } + if (target == PropertiesView::FocusEditor && widgets.editor) { + widgets.editor->setFocus(); + return widgets.editor; + } + if (target == PropertiesView::FocusLabel && widgets.label) { + widgets.label->setFocus(); + return widgets.label; + } + if (auto groupProperty = qobject_cast(subProperty)) { + groupProperty->setExpanded(true); + if (widgets.children && !groupProperty->subProperties().isEmpty()) + focusPropertyImpl(groupProperty, groupProperty->subProperties().first(), target); + return widgets.children; + } + return nullptr; + } else if (auto groupProperty = qobject_cast(subProperty)) { + if (widgets.children) { + if (auto w = focusPropertyImpl(groupProperty, property, target)) { + groupProperty->setExpanded(true); + return w; + } + } + } + } + + return nullptr; +} + +void PropertiesView::createPropertyWidgets(Property *property, QWidget *parent, int level, + QVBoxLayout *layout, int index) +{ + auto widgets = createPropertyWidgets(property, parent, level); + m_propertyWidgets.insert(property, widgets); + layout->insertWidget(index, widgets.rowWidget); +} + +PropertiesView::PropertyWidgets PropertiesView::createPropertyWidgets(Property *property, + QWidget *parent, + int level) +{ + Q_ASSERT(!m_propertyWidgets.contains(property)); + + PropertyWidgets widgets; + widgets.level = level; + + const auto displayMode = property->displayMode(); + + connect(property, &Property::destroyed, this, [this](QObject *object) { + removeProperty(static_cast(object)); + }); + + if (displayMode == Property::DisplayMode::ChildrenOnly) { + QWidget *children; + if (auto groupProperty = qobject_cast(property)) + children = createChildrenWidget(groupProperty, parent, level); + else + children = new QWidget(parent); + + widgets.children = children; + widgets.rowWidget = children; + return widgets; + } + + const auto halfSpacing = Utils::dpiScaled(2); + + if (displayMode == Property::DisplayMode::Separator) { + auto rowWidget = new QWidget(parent); + auto rowLayout = new QHBoxLayout(rowWidget); + + auto separator = new QFrame(rowWidget); + rowLayout->setContentsMargins(0, halfSpacing, 0, halfSpacing); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Plain); + separator->setForegroundRole(QPalette::Mid); + rowLayout->addWidget(separator); + + widgets.rowWidget = rowWidget; + return widgets; + } + + auto rowWidget = new PropertyWidget(property, parent); + auto rowLayout = new QHBoxLayout(rowWidget); + + widgets.rowWidget = rowWidget; + + rowLayout->setSpacing(halfSpacing * 2); + rowLayout->setContentsMargins(0, 0, 0, 0); + + rowWidget->setSelectable(property->actions().testFlag(Property::Action::Select)); + + connect(rowWidget, &PropertyWidget::clicked, this, [=](Qt::KeyboardModifiers modifiers) { + if (modifiers & Qt::ShiftModifier) { + // Select range between m_selectionStart and clicked property + if (assignSelectedPropertiesRange(m_root, m_selectionStart, property)) + emit selectedPropertiesChanged(); + return; + } else if (modifiers & Qt::ControlModifier) { + // Toggle selection + if (property->actions().testFlag(Property::Action::Select)) { + property->setSelected(!property->isSelected()); + emit selectedPropertiesChanged(); + } + } else { + // Select only the clicked property + if (property->actions().testFlag(Property::Action::Select)) + setSelectedProperties({property}); + else + setSelectedProperties({}); + } + + m_selectionStart = property; + }); + + widgets.label = property->createLabel(level, rowWidget); // todo: fix level + + if (displayMode != Property::DisplayMode::Header) { + if (isLeftToRight()) + rowLayout->setContentsMargins(0, halfSpacing, halfSpacing * 2, halfSpacing); + else + rowLayout->setContentsMargins(halfSpacing * 2, halfSpacing, 0, halfSpacing); + } + + if (widgets.label) + rowLayout->addWidget(widgets.label, LabelStretch, Qt::AlignTop); + + auto editorLayout = new QHBoxLayout; + widgets.editor = property->createEditor(rowWidget); + + if (widgets.editor) { + widgets.editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + editorLayout->addWidget(widgets.editor, EditorStretch, Qt::AlignTop); + rowLayout->addLayout(editorLayout, EditorStretch); + } else { + rowLayout->addLayout(editorLayout, 0); + } + + widgets.resetButton = new QToolButton(rowWidget); + widgets.resetButton->setToolTip(tr("Reset")); + widgets.resetButton->setIcon(m_resetIcon); + widgets.resetButton->setAutoRaise(true); + widgets.resetButton->setEnabled(property->isModified()); + Utils::setThemeIcon(widgets.resetButton, "edit-clear"); + editorLayout->addWidget(widgets.resetButton, 0, Qt::AlignTop); + connect(widgets.resetButton, &QAbstractButton::clicked, property, &Property::resetRequested); + connect(property, &Property::modifiedChanged, widgets.resetButton, &QWidget::setEnabled); + + widgets.removeButton = new QToolButton(rowWidget); + widgets.removeButton->setToolTip(tr("Remove")); + widgets.removeButton->setIcon(m_removeIcon); + widgets.removeButton->setAutoRaise(true); + Utils::setThemeIcon(widgets.removeButton, "remove"); + editorLayout->addWidget(widgets.removeButton, 0, Qt::AlignTop); + connect(widgets.removeButton, &QAbstractButton::clicked, property, &Property::removeRequested); + + widgets.addButton = new QToolButton(rowWidget); + widgets.addButton->setToolTip(tr("Add")); + widgets.addButton->setIcon(m_addIcon); + widgets.addButton->setAutoRaise(true); + widgets.addButton->setFocusPolicy(Qt::StrongFocus); // needed for AddValueProperty + Utils::setThemeIcon(widgets.addButton, "add"); + editorLayout->addWidget(widgets.addButton, 0, Qt::AlignTop); + connect(widgets.addButton, &QAbstractButton::clicked, property, &Property::addRequested); + + if (auto groupProperty = qobject_cast(property)) { + auto containerWidget = new QWidget(parent); + containerWidget->setFocusProxy(rowWidget); + + auto containerLayout = new QVBoxLayout(containerWidget); + containerLayout->setContentsMargins(0, 0, 0, 0); + containerLayout->setSpacing(halfSpacing); + containerLayout->addWidget(rowWidget); + + connect(groupProperty, &GroupProperty::expandedChanged, this, [=](bool expanded) { + // Need to operate on a copy, because the reference might get invalidated + PropertyWidgets widgets = m_propertyWidgets.value(groupProperty); + setPropertyChildrenExpanded(widgets, groupProperty, containerLayout, expanded); + m_propertyWidgets.insert(groupProperty, widgets); + }); + + setPropertyChildrenExpanded(widgets, groupProperty, containerLayout, groupProperty->isExpanded()); + + widgets.rowWidget = containerWidget; + } + + updatePropertyEnabled(widgets, property->isEnabled()); + updatePropertyActions(widgets, property->actions()); + + connect(property, &Property::enabledChanged, this, [=] (bool enabled) { + updatePropertyEnabled(m_propertyWidgets[property], enabled); + }); + connect(property, &Property::actionsChanged, this, [=] (Property::Actions actions) { + updatePropertyActions(m_propertyWidgets[property], actions); + }); + + return widgets; +} + +QWidget *PropertiesView::createChildrenWidget(GroupProperty *groupProperty, + QWidget *parent, int level) +{ + auto children = new QWidget(parent); + children->setEnabled(groupProperty->isEnabled()); + children->setBackgroundRole((level % 2) ? QPalette::Base + : QPalette::AlternateBase); + children->setAutoFillBackground(level > 0); + + auto layout = new QVBoxLayout(children); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + connect(groupProperty, &Property::enabledChanged, + children, &QWidget::setEnabled); + + for (auto property : groupProperty->subProperties()) + createPropertyWidgets(property, children, level, layout, layout->count()); + + connect(groupProperty, &GroupProperty::propertyAdded, + this, [=](int index, Property *property) { + createPropertyWidgets(property, children, level, layout, index); + }); + + return children; +} + +void PropertiesView::setPropertyChildrenExpanded(PropertyWidgets &widgets, + GroupProperty *groupProperty, + QVBoxLayout *rowVerticalLayout, + bool expanded) +{ + // Create the children editor on-demand + if (expanded && !widgets.children) { + const auto displayMode = groupProperty->displayMode(); + const auto level = displayMode == Property::DisplayMode::Default ? widgets.level + 1 + : widgets.level; + + widgets.children = createChildrenWidget(groupProperty, widgets.rowWidget, level); + + const auto halfSpacing = Utils::dpiScaled(2); + const auto top = displayMode == Property::DisplayMode::Header ? 0 : halfSpacing; + const auto bottom = halfSpacing; + widgets.children->setContentsMargins(0, top, 0, bottom); + + rowVerticalLayout->addWidget(widgets.children); + } + + if (widgets.children) { + widgets.children->setVisible(expanded); + + // needed to avoid flickering when hiding the editor + if (!expanded) { + QWidget *widget = widgets.rowWidget; + while (widget && widget->layout()) { + widget->layout()->activate(); + widget = widget->parentWidget(); + } + } + } +} + +void PropertiesView::updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled) +{ + if (widgets.label) + widgets.label->setEnabled(enabled); + if (widgets.editor) + widgets.editor->setEnabled(enabled); + if (widgets.children) + widgets.children->setEnabled(enabled); +} + +void PropertiesView::updatePropertyActions(const PropertyWidgets &widgets, + Property::Actions actions) +{ + if (auto rowWidget = qobject_cast(widgets.rowWidget)) + rowWidget->setSelectable(actions.testFlag(Property::Action::Select)); + + widgets.resetButton->setVisible(actions.testFlag(Property::Action::Reset)); + widgets.removeButton->setVisible(actions.testFlag(Property::Action::Remove)); + widgets.addButton->setVisible(actions.testFlag(Property::Action::Add)); + + widgets.addButton->setEnabled(!actions.testFlag(Property::Action::AddDisabled)); +} + } // namespace Tiled #include "moc_varianteditor.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/varianteditor.h index 0fe33c35e9..8cc46d470c 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/varianteditor.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ class QVBoxLayout; namespace Tiled { +class GroupProperty; class PropertyLabel; /** @@ -45,6 +47,7 @@ class Property : public QObject Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged) + Q_PROPERTY(bool selected READ isSelected WRITE setSelected NOTIFY selectedChanged) Q_PROPERTY(Actions actions READ actions WRITE setActions NOTIFY actionsChanged) public: @@ -61,6 +64,7 @@ class Property : public QObject Remove = 0x02, Add = 0x04, AddDisabled = Add | 0x08, + Select = 0x10, }; Q_DECLARE_FLAGS(Actions, Action) @@ -81,9 +85,14 @@ class Property : public QObject bool isModified() const { return m_modified; } void setModified(bool modified); + bool isSelected() const { return m_selected; } + void setSelected(bool selected); + Actions actions() const { return m_actions; } void setActions(Actions actions); + GroupProperty *parentProperty() const { return m_parent; } + virtual DisplayMode displayMode() const { return DisplayMode::Default; } virtual QWidget *createLabel(int level, QWidget *parent); @@ -95,6 +104,7 @@ class Property : public QObject void valueChanged(); void enabledChanged(bool enabled); void modifiedChanged(bool modified); + void selectedChanged(bool selected); void actionsChanged(Actions actions); void resetRequested(); @@ -110,7 +120,9 @@ class Property : public QObject QString m_toolTip; bool m_enabled = true; bool m_modified = false; + bool m_selected = false; Actions m_actions; + GroupProperty *m_parent = nullptr; }; class Separator final : public Property @@ -174,6 +186,8 @@ class GroupProperty : public Property void insertProperty(int index, Property *property) { + property->m_parent = this; + m_subProperties.insert(index, property); emit propertyAdded(index, property); } @@ -436,7 +450,7 @@ struct EnumData template EnumData enumData() { - return {{}}; + return {}; } /** @@ -492,80 +506,79 @@ class EnumProperty : public BaseEnumProperty }; -class VariantEditor : public QWidget +/** + * A scrollable view that displays a tree of properties. + */ +class PropertiesView : public QScrollArea { Q_OBJECT public: - VariantEditor(QWidget *parent = nullptr); + explicit PropertiesView(QWidget *parent = nullptr); - void clear(); - void addProperty(Property *property); - void insertProperty(int index, Property *property); - void removeProperty(Property *property); + void setRootProperty(GroupProperty *root); + + QList selectedProperties() const; + void setSelectedProperties(const QList &properties); enum FocusTarget { + FocusRow, FocusLabel, FocusEditor, }; - QWidget *focusProperty(Property *property, FocusTarget target); + bool focusProperty(Property *property, FocusTarget target = FocusEditor); + +signals: + void selectedPropertiesChanged(); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; - void setLevel(int level); + void keyPressEvent(QKeyEvent *event) override; private: - static constexpr int LabelStretch = 1; - static constexpr int EditorStretch = 1; + bool focusNextPrevProperty(Property *property, bool next, bool shiftPressed); + + void removeProperty(Property *property); + + QWidget *focusPropertyImpl(GroupProperty *group, Property *property, FocusTarget target); struct PropertyWidgets { - QWidget *rowWidget = nullptr; - QVBoxLayout *childrenLayout = nullptr; + int level = 0; + QWidget *rowWidget = nullptr; // The top-level widget for this property QWidget *label = nullptr; QWidget *editor = nullptr; QToolButton *resetButton = nullptr; QToolButton *removeButton = nullptr; QToolButton *addButton = nullptr; - VariantEditor *children = nullptr; + QWidget *children = nullptr; // The widget that contains the children of a group property }; - QWidget *createPropertyWidget(Property *property); + void createPropertyWidgets(Property *property, QWidget *parent, int level, + QVBoxLayout *layout, int index); + PropertyWidgets createPropertyWidgets(Property *property, QWidget *parent, int level); + QWidget *createChildrenWidget(GroupProperty *groupProperty, QWidget *parent, int level); - void setPropertyChildrenExpanded(GroupProperty *groupProperty, bool expanded); + void setPropertyChildrenExpanded(PropertyWidgets &widgets, + GroupProperty *groupProperty, QVBoxLayout *rowVerticalLayout, + bool expanded); void updatePropertyEnabled(const PropertyWidgets &widgets, bool enabled); void updatePropertyActions(const PropertyWidgets &widgets, Property::Actions actions); + static constexpr int LabelStretch = 1; + static constexpr int EditorStretch = 1; + QIcon m_resetIcon; QIcon m_removeIcon; QIcon m_addIcon; - QVBoxLayout *m_layout; + QPointer m_root; + QVBoxLayout *m_rootLayout; + QPointer m_selectionStart; QHash m_propertyWidgets; - int m_level = 0; -}; - -class VariantEditorView : public QScrollArea -{ - Q_OBJECT - -public: - explicit VariantEditorView(QWidget *parent = nullptr); - - void clear() - { m_editor->clear(); } - - void addProperty(Property *property) - { m_editor->addProperty(property); } - - void focusProperty(Property *property, - VariantEditor::FocusTarget target = VariantEditor::FocusEditor); - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - -private: - VariantEditor *m_editor; }; } // namespace Tiled diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index 076b85d4a3..5fbe6f0a5a 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -191,10 +191,10 @@ bool VariantMapProperty::createOrUpdateProperty(int index, if (property) { if (mValue.contains(name)) { property->setEnabled(true); - property->setActions(Property::Action::Remove); + property->setActions(Property::Action::Select | Property::Action::Remove); } else { property->setEnabled(false); - property->setActions(Property::Action::Add); + property->setActions(Property::Action::Select | Property::Action::Add); } updateModifiedRecursively(property, newValue); diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h index f0d8d40f23..b0ccab00fa 100644 --- a/src/tiled/variantmapproperty.h +++ b/src/tiled/variantmapproperty.h @@ -44,6 +44,10 @@ class VariantMapProperty : public GroupProperty const QVariantMap &value() const { return mValue; } + void removeMember(const QString &name); + void addMember(const QString &name, const QVariant &value); + void setClassMember(const QStringList &path, const QVariant &value); + Property *property(const QString &name) const; signals: @@ -70,10 +74,6 @@ class VariantMapProperty : public GroupProperty const ClassPropertyType &classType, std::function get); - void removeMember(const QString &name); - void addMember(const QString &name, const QVariant &value); - void setClassMember(const QStringList &path, const QVariant &value); - void updateModifiedRecursively(Property *property, const QVariant &value); void emitValueChangedRecursively(Property *property); From 681d4ea89a7d3fd4968df80f0210a69f29e72b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Fri, 29 Nov 2024 17:06:25 +0100 Subject: [PATCH 72/78] Renamed varianteditor.{h,cpp} to propertiesview.{h,cpp} To match the rename in the last commit. The term "VariantEditor" referred to the initial approach of using QVariant for all properties. Now "PropertiesView" is more appropriate, since it is a view based on the type-agnostic "Property" hierarchy. --- src/tiled/libtilededitor.qbs | 4 ++-- src/tiled/projectpropertiesdialog.cpp | 2 +- src/tiled/propertiesdock.cpp | 2 +- src/tiled/{varianteditor.cpp => propertiesview.cpp} | 6 +++--- src/tiled/{varianteditor.h => propertiesview.h} | 2 +- src/tiled/propertieswidget.cpp | 2 +- src/tiled/propertyeditorwidgets.cpp | 2 +- src/tiled/propertytypeseditor.cpp | 2 +- src/tiled/variantmapproperty.h | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) rename src/tiled/{varianteditor.cpp => propertiesview.cpp} (99%) rename src/tiled/{varianteditor.h => propertiesview.h} (99%) diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 6d73e57a8f..a09578dc3d 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -396,6 +396,8 @@ DynamicLibrary { "projectpropertiesdialog.ui", "propertiesdock.cpp", "propertiesdock.h", + "propertiesview.cpp", + "propertiesview.h", "propertieswidget.cpp", "propertieswidget.h", "propertyeditorwidgets.cpp", @@ -546,8 +548,6 @@ DynamicLibrary { "undodock.h", "utils.cpp", "utils.h", - "varianteditor.cpp", - "varianteditor.h", "variantmapproperty.cpp", "variantmapproperty.h", "wangbrush.cpp", diff --git a/src/tiled/projectpropertiesdialog.cpp b/src/tiled/projectpropertiesdialog.cpp index 09823b937b..50594027bb 100644 --- a/src/tiled/projectpropertiesdialog.cpp +++ b/src/tiled/projectpropertiesdialog.cpp @@ -24,9 +24,9 @@ #include "mapformat.h" #include "project.h" #include "projectdocument.h" +#include "propertiesview.h" #include "tiled.h" #include "utils.h" -#include "varianteditor.h" #include #include diff --git a/src/tiled/propertiesdock.cpp b/src/tiled/propertiesdock.cpp index 58607a0561..87d0aaad9d 100644 --- a/src/tiled/propertiesdock.cpp +++ b/src/tiled/propertiesdock.cpp @@ -20,8 +20,8 @@ #include "propertiesdock.h" +#include "propertiesview.h" #include "propertieswidget.h" -#include "varianteditor.h" #include diff --git a/src/tiled/varianteditor.cpp b/src/tiled/propertiesview.cpp similarity index 99% rename from src/tiled/varianteditor.cpp rename to src/tiled/propertiesview.cpp index 97e5ca223f..4ae057add8 100644 --- a/src/tiled/varianteditor.cpp +++ b/src/tiled/propertiesview.cpp @@ -1,5 +1,5 @@ /* - * varianteditor.cpp + * propertiesview.cpp * Copyright 2024, Thorbjørn Lindeijer * * This file is part of Tiled. @@ -18,7 +18,7 @@ * this program. If not, see . */ -#include "varianteditor.h" +#include "propertiesview.h" #include "colorbutton.h" #include "fileedit.h" @@ -1433,4 +1433,4 @@ void PropertiesView::updatePropertyActions(const PropertyWidgets &widgets, } // namespace Tiled -#include "moc_varianteditor.cpp" +#include "moc_propertiesview.cpp" diff --git a/src/tiled/varianteditor.h b/src/tiled/propertiesview.h similarity index 99% rename from src/tiled/varianteditor.h rename to src/tiled/propertiesview.h index 8cc46d470c..2fc1d0aef1 100644 --- a/src/tiled/varianteditor.h +++ b/src/tiled/propertiesview.h @@ -1,5 +1,5 @@ /* - * varianteditor.h + * propertiesview.h * Copyright 2024, Thorbjørn Lindeijer * * This file is part of Tiled. diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index d7d647b8c6..3be4239a0e 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -37,13 +37,13 @@ #include "objectgroup.h" #include "objecttemplate.h" #include "preferences.h" +#include "propertiesview.h" #include "propertyeditorwidgets.h" #include "tilesetchanges.h" #include "tilesetdocument.h" #include "tilesetparametersedit.h" #include "transformmapobjects.h" #include "utils.h" -#include "varianteditor.h" #include "variantmapproperty.h" #include "wangoverlay.h" diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index b101cd3ca3..b4c9880a71 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -20,8 +20,8 @@ #include "propertyeditorwidgets.h" +#include "propertiesview.h" #include "utils.h" -#include "varianteditor.h" #include #include diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 7e82cf4264..4b3063bfa8 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -26,11 +26,11 @@ #include "preferences.h" #include "project.h" #include "projectmanager.h" +#include "propertiesview.h" #include "propertytypesmodel.h" #include "savefile.h" #include "session.h" #include "utils.h" -#include "varianteditor.h" #include "variantmapproperty.h" #include diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h index b0ccab00fa..6c8304f44e 100644 --- a/src/tiled/variantmapproperty.h +++ b/src/tiled/variantmapproperty.h @@ -20,8 +20,8 @@ #pragma once +#include "propertiesview.h" #include "propertytype.h" -#include "varianteditor.h" #include From edc9dac16976466148e35cf55515500924213c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Mon, 2 Dec 2024 17:59:08 +0100 Subject: [PATCH 73/78] Fixed color widget and reset button alignment Now they both scale vertically to cover the minimum size of both. --- src/tiled/propertiesview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tiled/propertiesview.cpp b/src/tiled/propertiesview.cpp index 4ae057add8..782fbb1c24 100644 --- a/src/tiled/propertiesview.cpp +++ b/src/tiled/propertiesview.cpp @@ -447,7 +447,7 @@ QWidget *ColorProperty::createEditor(QWidget *parent) auto colorButton = new ColorButton(editor); colorButton->setShowAlphaChannel(m_alpha); - colorButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + colorButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); QIcon resetIcon(QStringLiteral(":/images/16/edit-clear.png")); resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); @@ -455,6 +455,7 @@ QWidget *ColorProperty::createEditor(QWidget *parent) auto resetButton = new QToolButton(editor); resetButton->setIcon(resetIcon); resetButton->setToolTip(tr("Clear Color")); + resetButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); Utils::setThemeIcon(resetButton, "edit-clear"); layout->addWidget(colorButton); From b7c230aa728c8b26b40d62544ad47dbc3afe6654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Tue, 3 Dec 2024 17:19:31 +0100 Subject: [PATCH 74/78] Don't copy an empty array when no properties are selected Better to not touch the clipboard at all in this case. --- src/tiled/propertieswidget.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 3be4239a0e..63021b8ffd 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2462,9 +2462,11 @@ bool PropertiesWidget::copyProperties() if (!object) return false; - Properties properties; - const auto selectedProperties = mPropertiesView->selectedProperties(); + if (selectedProperties.isEmpty()) + return false; + + Properties properties; for (auto property : selectedProperties) { const QString name = property->name(); const QVariant value = object->property(name); From 8a36db584daecc01b1c265c02f83e15ba3be25bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 4 Dec 2024 18:56:37 +0100 Subject: [PATCH 75/78] Select pasted custom properties and expand the group When pasting custom properties, we now make sure that the Custom Properties group is expanded and select the pasted properties for some additional feedback. Also focus the first pasted property, which makes sure at least one of them is visible. --- src/tiled/propertieswidget.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 63021b8ffd..621b2d41ff 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2516,6 +2516,20 @@ void PropertiesWidget::pasteProperties() undoStack->push(command); undoStack->endMacro(); + + // Make sure Custom Properties are expanded + mCustomProperties->setExpanded(true); + } + + // Select the pasted properties + QList selectedProperties; + for (const QString &name : pastedProperties.keys()) { + if (auto property = mCustomProperties->property(name)) + selectedProperties.append(property); + } + if (!selectedProperties.isEmpty()) { + mPropertiesView->focusProperty(selectedProperties.first(), PropertiesView::FocusRow); + mPropertiesView->setSelectedProperties(selectedProperties); } } From 73e6373ce34433f222f5e5956ed69be1c4ffed2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Thu, 5 Dec 2024 11:41:42 +0100 Subject: [PATCH 76/78] Add custom property when pressing Enter with type combo focused This change also disables Enter for opening the combo box popup, fixing the rather broken behavior (at least on Linux) that pressing Enter to select a type will immediately re-open the popup. That only applies to combo boxes in the Properties view, though. --- src/tiled/propertyeditorwidgets.cpp | 12 ++++++++++++ src/tiled/propertyeditorwidgets.h | 5 +++++ src/tiled/variantmapproperty.cpp | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index b4c9880a71..625bb33a1f 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -288,6 +288,18 @@ bool ComboBox::event(QEvent *event) return QComboBox::event(event); } +void ComboBox::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + emit returnPressed(); + return; + } + + QComboBox::keyPressEvent(event); +} + void ComboBox::wheelEvent(QWheelEvent *event) { if (!hasFocus()) diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index c08b86cc7e..bfe3c1cc69 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -70,8 +70,13 @@ class ComboBox : public QComboBox public: ComboBox(QWidget *parent = nullptr); +signals: + /** This signal is emitted when the Return or Enter key is pressed. */ + void returnPressed(); + protected: bool event(QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; void wheelEvent(QWheelEvent *event) override; }; diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index 5fbe6f0a5a..b7b1a41263 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -586,6 +586,10 @@ QWidget *AddValueProperty::createEditor(QWidget *parent) m_value = typeBox->itemData(index); session::propertyType = typeBox->currentText(); }); + connect(typeBox, &ComboBox::returnPressed, this, [this] { + if (!name().isEmpty()) + emit addRequested(); + }); typeBox->installEventFilter(this); From 8eb447e22e73cf95ff05dd83432e1ac36a7662df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 11 Dec 2024 16:58:25 +0100 Subject: [PATCH 77/78] Moved the context menu implementation back to PropertiesWidget This makes the property-specific actions like "Go to Object" and "Open Containing Folder" available again, as well as the Cut, Copy and Paste actions. For now, the Custom Types Editor has no context menu anymore... Property widgets are now removed before their property is deleted to avoid a crash when right-clicking the AddValueProperty, since that caused the focus to be lost and thus the removal of the property while at the same time triggering a context menu for it. --- src/tiled/propertiesview.cpp | 75 ++++++++++++++++++++----- src/tiled/propertiesview.h | 28 +++++++++- src/tiled/propertieswidget.cpp | 87 +++++++++++++++++------------ src/tiled/propertieswidget.h | 4 ++ src/tiled/propertyeditorwidgets.cpp | 16 +----- src/tiled/propertyeditorwidgets.h | 8 ++- src/tiled/propertytypeseditor.cpp | 2 - src/tiled/variantmapproperty.cpp | 73 ++++-------------------- src/tiled/variantmapproperty.h | 7 --- 9 files changed, 158 insertions(+), 142 deletions(-) diff --git a/src/tiled/propertiesview.cpp b/src/tiled/propertiesview.cpp index 782fbb1c24..0c92c7df86 100644 --- a/src/tiled/propertiesview.cpp +++ b/src/tiled/propertiesview.cpp @@ -28,11 +28,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -136,6 +138,12 @@ QWidget *GroupProperty::createLabel(int level, QWidget *parent) return label; } +void GroupProperty::addContextMenuActions(QMenu *menu) +{ + menu->addAction(tr("Expand All"), this, &GroupProperty::expandAll); + menu->addAction(tr("Collapse All"), this, &GroupProperty::collapseAll); +} + void GroupProperty::setExpanded(bool expanded) { if (m_expanded != expanded) { @@ -224,6 +232,18 @@ QWidget *UrlProperty::createEditor(QWidget *parent) return editor; } +void UrlProperty::addContextMenuActions(QMenu *menu) +{ + const QString localFile = value().toLocalFile(); + + if (!localFile.isEmpty()) { + Utils::addOpenContainingFolderAction(*menu, localFile); + + if (QFileInfo { localFile }.isFile()) + Utils::addOpenWithSystemEditorAction(*menu, localFile); + } +} + QWidget *IntProperty::createEditor(QWidget *parent) { auto widget = new QWidget(parent); @@ -969,6 +989,12 @@ bool PropertiesView::focusProperty(Property *property, FocusTarget target) return true; } +Property *PropertiesView::focusedProperty() const +{ + auto propertyWidget = qobject_cast(focusWidget()); + return propertyWidget ? propertyWidget->property() : nullptr; +} + bool PropertiesView::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::Show) { @@ -986,10 +1012,18 @@ bool PropertiesView::eventFilter(QObject *watched, QEvent *event) return QScrollArea::eventFilter(watched, event); } +void PropertiesView::mousePressEvent(QMouseEvent *event) +{ + // If the view gets mouse press then no PropertyWidget was clicked + if (event->modifiers() == Qt::NoModifier) + setSelectedProperties({}); + + QScrollArea::mousePressEvent(event); +} + void PropertiesView::keyPressEvent(QKeyEvent *event) { - auto propertyWidget = qobject_cast(focusWidget()); - auto property = propertyWidget ? propertyWidget->property() : nullptr; + auto property = focusedProperty(); const auto key = event->key(); const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; @@ -1092,10 +1126,7 @@ bool PropertiesView::focusNextPrevProperty(Property *property, bool next, bool s return false; } -/** - * Removes the given property from the editor. The property is not deleted. - */ -void PropertiesView::removeProperty(Property *property) +void PropertiesView::deletePropertyWidgets(Property *property) { auto it = m_propertyWidgets.constFind(property); Q_ASSERT(it != m_propertyWidgets.constEnd()); @@ -1113,7 +1144,7 @@ void PropertiesView::removeProperty(Property *property) rowWidget->deleteLater(); - m_propertyWidgets.erase(it); + forgetProperty(property); // This appears to be necessary to avoid flickering due to relayouting // not being done before the next paint. @@ -1121,8 +1152,21 @@ void PropertiesView::removeProperty(Property *property) widget->layout()->activate(); widget = widget->parentWidget(); } +} + +void PropertiesView::forgetProperty(Property *property) +{ + m_propertyWidgets.remove(property); + + if (property == m_selectionStart) + m_selectionStart = nullptr; property->disconnect(this); + + if (GroupProperty *groupProperty = qobject_cast(property)) { + for (auto subProperty : groupProperty->subProperties()) + forgetProperty(subProperty); + } } /** @@ -1197,10 +1241,6 @@ PropertiesView::PropertyWidgets PropertiesView::createPropertyWidgets(Property * const auto displayMode = property->displayMode(); - connect(property, &Property::destroyed, this, [this](QObject *object) { - removeProperty(static_cast(object)); - }); - if (displayMode == Property::DisplayMode::ChildrenOnly) { QWidget *children; if (auto groupProperty = qobject_cast(property)) @@ -1240,7 +1280,7 @@ PropertiesView::PropertyWidgets PropertiesView::createPropertyWidgets(Property * rowWidget->setSelectable(property->actions().testFlag(Property::Action::Select)); - connect(rowWidget, &PropertyWidget::clicked, this, [=](Qt::KeyboardModifiers modifiers) { + connect(rowWidget, &PropertyWidget::mousePressed, this, [=](Qt::MouseButton button, Qt::KeyboardModifiers modifiers) { if (modifiers & Qt::ShiftModifier) { // Select range between m_selectionStart and clicked property if (assignSelectedPropertiesRange(m_root, m_selectionStart, property)) @@ -1254,10 +1294,12 @@ PropertiesView::PropertyWidgets PropertiesView::createPropertyWidgets(Property * } } else { // Select only the clicked property - if (property->actions().testFlag(Property::Action::Select)) - setSelectedProperties({property}); - else + if (property->actions().testFlag(Property::Action::Select)) { + if (button == Qt::LeftButton || !property->isSelected()) + setSelectedProperties({property}); + } else { setSelectedProperties({}); + } } m_selectionStart = property; @@ -1371,6 +1413,9 @@ QWidget *PropertiesView::createChildrenWidget(GroupProperty *groupProperty, createPropertyWidgets(property, children, level, layout, index); }); + connect(groupProperty, &GroupProperty::propertyRemoved, + this, &PropertiesView::deletePropertyWidgets); + return children; } diff --git a/src/tiled/propertiesview.h b/src/tiled/propertiesview.h index 2fc1d0aef1..58f1ddffac 100644 --- a/src/tiled/propertiesview.h +++ b/src/tiled/propertiesview.h @@ -29,6 +29,7 @@ #include class QHBoxLayout; +class QMenu; class QToolButton; class QVBoxLayout; @@ -97,6 +98,7 @@ class Property : public QObject virtual QWidget *createLabel(int level, QWidget *parent); virtual QWidget *createEditor(QWidget *parent) = 0; + virtual void addContextMenuActions(QMenu *) {} signals: void nameChanged(const QString &name); @@ -165,6 +167,7 @@ class GroupProperty : public Property QWidget *createLabel(int level, QWidget *parent) override; QWidget *createEditor(QWidget */* parent */) override { return nullptr; } + void addContextMenuActions(QMenu *) override; void setHeader(bool header) { m_header = header; } @@ -194,10 +197,23 @@ class GroupProperty : public Property void deleteProperty(Property *property) { - m_subProperties.removeOne(property); + removeProperty(property); delete property; } + /** + * Removes the given property from this group. Ownership of the property + * is transferred to the caller. + */ + void removeProperty(Property *property) + { + if (!m_subProperties.removeOne(property)) + return; + + property->m_parent = nullptr; + emit propertyRemoved(property); + } + int indexOfProperty(Property *property) const { return m_subProperties.indexOf(property); @@ -210,6 +226,7 @@ class GroupProperty : public Property signals: void expandedChanged(bool expanded); void propertyAdded(int index, Property *property); + void propertyRemoved(Property *property); private: bool m_header = true; @@ -270,9 +287,13 @@ struct MultilineStringProperty : StringProperty struct UrlProperty : PropertyTemplate { using PropertyTemplate::PropertyTemplate; + QWidget *createEditor(QWidget *parent) override; + void addContextMenuActions(QMenu *) override; + void setFilter(const QString &filter) { m_filter = filter; } void setIsDirectory(bool isDirectory) { m_isDirectory = isDirectory; } + private: QString m_filter; bool m_isDirectory = false; @@ -528,6 +549,7 @@ class PropertiesView : public QScrollArea }; bool focusProperty(Property *property, FocusTarget target = FocusEditor); + Property *focusedProperty() const; signals: void selectedPropertiesChanged(); @@ -535,12 +557,14 @@ class PropertiesView : public QScrollArea protected: bool eventFilter(QObject *watched, QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; private: bool focusNextPrevProperty(Property *property, bool next, bool shiftPressed); - void removeProperty(Property *property); + void deletePropertyWidgets(Property *property); + void forgetProperty(Property *property); QWidget *focusPropertyImpl(GroupProperty *group, Property *property, FocusTarget target); diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp index 621b2d41ff..0a796d7be6 100644 --- a/src/tiled/propertieswidget.cpp +++ b/src/tiled/propertieswidget.cpp @@ -2160,21 +2160,29 @@ class WangColorProperties : public ObjectProperties PropertiesWidget::PropertiesWidget(QWidget *parent) : QWidget{parent} + , mResetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) + , mRemoveIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) + , mAddIcon(QIcon(QStringLiteral(":/images/16/add.png"))) + , mRenameIcon(QIcon(QLatin1String(":/images/16/rename.png"))) , mRootProperty(new GroupProperty()) , mCustomProperties(new CustomProperties(mRootProperty)) , mPropertiesView(new PropertiesView(this)) { + mResetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); + mRemoveIcon.addFile(QStringLiteral(":/images/22/remove.png")); + mAddIcon.addFile(QStringLiteral(":/images/22/add.png")); + mRootProperty->addProperty(mCustomProperties); mActionAddProperty = new QAction(this); mActionAddProperty->setEnabled(false); - mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); + mActionAddProperty->setIcon(mAddIcon); connect(mActionAddProperty, &QAction::triggered, this, &PropertiesWidget::showAddValueProperty); mActionRemoveProperty = new QAction(this); mActionRemoveProperty->setEnabled(false); - mActionRemoveProperty->setIcon(QIcon(QLatin1String(":/images/16/remove.png"))); + mActionRemoveProperty->setIcon(mRemoveIcon); mActionRemoveProperty->setShortcuts(QKeySequence::Delete); mActionRemoveProperty->setPriority(QAction::LowPriority); connect(mActionRemoveProperty, &QAction::triggered, @@ -2182,7 +2190,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) mActionRenameProperty = new QAction(this); mActionRenameProperty->setEnabled(false); - mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); + mActionRenameProperty->setIcon(mRenameIcon); mActionRenameProperty->setPriority(QAction::LowPriority); connect(mActionRenameProperty, &QAction::triggered, this, &PropertiesWidget::renameSelectedProperty); @@ -2213,9 +2221,6 @@ PropertiesWidget::PropertiesWidget(QWidget *parent) connect(mPropertiesView, &PropertiesView::selectedPropertiesChanged, this, &PropertiesWidget::updateActions); - connect(mCustomProperties, &VariantMapProperty::renameRequested, - this, &PropertiesWidget::renameProperty); - retranslateUi(); } @@ -2400,7 +2405,8 @@ void CustomProperties::refresh() // Suggest properties from selected objects. Properties suggestedProperties; - for (auto object : mDocument->currentObjects()) + const auto currentObjects = mDocument->currentObjects(); + for (auto object : currentObjects) if (object != mDocument->currentObject()) mergeProperties(suggestedProperties, object->properties()); @@ -2630,48 +2636,47 @@ void PropertiesWidget::renameProperty(const QString &name) void PropertiesWidget::showContextMenu(const QPoint &pos) { -#if 0 const Object *object = mDocument->currentObject(); if (!object) return; - const QList items = mPropertyBrowser->selectedItems(); - const bool customPropertiesSelected = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); + const auto properties = mPropertiesView->selectedProperties(); + const bool customPropertiesSelected = !properties.isEmpty(); bool currentObjectHasAllProperties = true; QStringList propertyNames; - for (QtBrowserItem *item : items) { - const QString propertyName = item->property()->propertyName(); - propertyNames.append(propertyName); + for (auto property : properties) { + propertyNames.append(property->name()); - if (!object->hasProperty(propertyName)) + if (!object->hasProperty(property->name())) currentObjectHasAllProperties = false; } - QMenu contextMenu(mPropertyBrowser); + QMenu contextMenu(mPropertiesView); - if (customPropertiesSelected && propertyNames.size() == 1) { - const auto value = object->resolvedProperty(propertyNames.first()); - if (value.userType() == filePathTypeId()) { - const FilePath filePath = value.value(); - const QString localFile = filePath.url.toLocalFile(); + // Add properties specific to the just clicked property + if (auto focusedProperty = mPropertiesView->focusedProperty()) { + focusedProperty->addContextMenuActions(&contextMenu); - if (!localFile.isEmpty()) { - Utils::addOpenContainingFolderAction(contextMenu, localFile); + // Provide the Add, Remove and Reset actions also here + if (const auto actions = focusedProperty->actions()) { + if (!contextMenu.isEmpty()) + contextMenu.addSeparator(); - if (QFileInfo { localFile }.isFile()) - Utils::addOpenWithSystemEditorAction(contextMenu, localFile); + // Note: No "Remove" added here since it's covered below + + if (actions.testFlag(Property::Action::Add)) { + QAction *add = contextMenu.addAction(mAddIcon, tr("Add"), + focusedProperty, &Property::addRequested); + add->setEnabled(!actions.testFlag(Property::Action::AddDisabled)); + Utils::setThemeIcon(add, "add"); } - } else if (value.userType() == objectRefTypeId()) { - if (auto mapDocument = qobject_cast(mDocument)) { - const DisplayObjectRef objectRef(value.value(), mapDocument); - - contextMenu.addAction(QCoreApplication::translate("Tiled::PropertiesDock", "Go to Object"), [=] { - if (auto object = objectRef.object()) { - objectRef.mapDocument->setSelectedObjects({object}); - emit objectRef.mapDocument->focusMapObjectRequested(object); - } - })->setEnabled(objectRef.object()); + + if (actions.testFlag(Property::Action::Reset)) { + QAction *reset = contextMenu.addAction(mResetIcon, tr("Reset"), + focusedProperty, &Property::resetRequested); + reset->setEnabled(focusedProperty->isModified()); + Utils::setThemeIcon(reset, "edit-clear"); } } } @@ -2747,12 +2752,12 @@ void PropertiesWidget::showContextMenu(const QPoint &pos) ActionManager::applyMenuExtensions(&contextMenu, MenuIds::propertiesViewProperties); - const QPoint globalPos = mPropertyBrowser->mapToGlobal(pos); + const QPoint globalPos = mPropertiesView->mapToGlobal(pos); const QAction *selectedItem = contextMenu.exec(globalPos); if (selectedItem && convertMenu && selectedItem->parentWidget() == convertMenu) { QUndoStack *undoStack = mDocument->undoStack(); - undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock", "Convert Property/Properties", nullptr, items.size())); + undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock", "Convert Property/Properties", nullptr, properties.size())); for (const QString &propertyName : propertyNames) { QVariant propertyValue = object->property(propertyName); @@ -2766,8 +2771,16 @@ void PropertiesWidget::showContextMenu(const QPoint &pos) } undoStack->endMacro(); + + // Restore selected properties + QList selectedProperties; + for (const QString &name : propertyNames) { + if (auto property = mCustomProperties->property(name)) + selectedProperties.append(property); + } + if (!selectedProperties.isEmpty()) + mPropertiesView->setSelectedProperties(selectedProperties); } -#endif } bool PropertiesWidget::event(QEvent *event) diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h index ce0dd22e95..ccb8f67302 100644 --- a/src/tiled/propertieswidget.h +++ b/src/tiled/propertieswidget.h @@ -84,6 +84,10 @@ public slots: void retranslateUi(); + QIcon mResetIcon; + QIcon mRemoveIcon; + QIcon mAddIcon; + QIcon mRenameIcon; Document *mDocument = nullptr; GroupProperty *mRootProperty = nullptr; ObjectProperties *mPropertiesObject = nullptr; diff --git a/src/tiled/propertyeditorwidgets.cpp b/src/tiled/propertyeditorwidgets.cpp index 625bb33a1f..c7f794a562 100644 --- a/src/tiled/propertyeditorwidgets.cpp +++ b/src/tiled/propertyeditorwidgets.cpp @@ -930,22 +930,8 @@ void PropertyWidget::paintEvent(QPaintEvent *event) void PropertyWidget::mousePressEvent(QMouseEvent *event) { - // Not 100% correct hack to make sure only one property is selected when - // the context menu opens. Handling of the context menu should probably be - // moved elsewhere. - if (event->button() == Qt::RightButton) - emit clicked(Qt::NoModifier); - else - emit clicked(event->modifiers()); - setFocus(Qt::MouseFocusReason); - - QWidget::mousePressEvent(event); -} - -void PropertyWidget::contextMenuEvent(QContextMenuEvent *event) -{ - emit m_property->contextMenuRequested(event->globalPos()); + emit mousePressed(event->button(), event->modifiers()); } } // namespace Tiled diff --git a/src/tiled/propertyeditorwidgets.h b/src/tiled/propertyeditorwidgets.h index bfe3c1cc69..d28e6a4e29 100644 --- a/src/tiled/propertyeditorwidgets.h +++ b/src/tiled/propertyeditorwidgets.h @@ -23,6 +23,7 @@ #include #include #include +#include #include class QLabel; @@ -373,15 +374,16 @@ class PropertyWidget : public QWidget bool isSelected() const { return m_selected; } signals: - void clicked(Qt::KeyboardModifiers modifiers); + void mousePressed(Qt::MouseButton button, Qt::KeyboardModifiers modifiers); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; - void contextMenuEvent(QContextMenuEvent *event) override; private: - Property *m_property; + // Protected by QPointer because PropertyWidget might outlive the Property + // for a short moment, due to delayed widget deletion. + QPointer m_property; bool m_selectable = false; bool m_selected = false; }; diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index 4b3063bfa8..54ab4e7dc6 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -953,8 +953,6 @@ void PropertyTypesEditor::addClassProperties() connect(mMembersProperty, &VariantMapProperty::valueChanged, this, &PropertyTypesEditor::classMembersChanged); - connect(mMembersProperty, &VariantMapProperty::renameRequested, - this, &PropertyTypesEditor::renameMember); connect(mMembersView, &PropertiesView::selectedPropertiesChanged, this, &PropertyTypesEditor::selectedMembersChanged); diff --git a/src/tiled/variantmapproperty.cpp b/src/tiled/variantmapproperty.cpp index b7b1a41263..99b1acf215 100644 --- a/src/tiled/variantmapproperty.cpp +++ b/src/tiled/variantmapproperty.cpp @@ -63,20 +63,23 @@ class ObjectRefProperty : public PropertyTemplate }); return editor; } + + void addContextMenuActions(QMenu *menu) override + { + auto objectRef = value(); + menu->addAction(QCoreApplication::translate("Tiled::PropertiesDock", "Go to Object"), [=] { + if (auto object = objectRef.object()) { + objectRef.mapDocument->setSelectedObjects({object}); + emit objectRef.mapDocument->focusMapObjectRequested(object); + } + })->setEnabled(objectRef.object()); + } }; VariantMapProperty::VariantMapProperty(const QString &name, QObject *parent) : GroupProperty(name, parent) - , m_resetIcon(QIcon(QStringLiteral(":/images/16/edit-clear.png"))) - , m_removeIcon(QIcon(QStringLiteral(":/images/16/remove.png"))) - , m_addIcon(QIcon(QStringLiteral(":/images/16/add.png"))) - , m_renameIcon(QIcon(QLatin1String(":/images/16/rename.png"))) { - m_resetIcon.addFile(QStringLiteral(":/images/24/edit-clear.png")); - m_removeIcon.addFile(QStringLiteral(":/images/22/remove.png")); - m_addIcon.addFile(QStringLiteral(":/images/22/add.png")); - connect(Preferences::instance(), &Preferences::propertyTypesChanged, this, &VariantMapProperty::propertyTypesChanged); } @@ -287,10 +290,6 @@ Property *VariantMapProperty::createProperty(const QStringList &path, property->setToolTip(QStringLiteral("%1 : %2") .arg(property->name(), typeName)); - - connect(property, &Property::contextMenuRequested, this, [=](const QPoint &globalPos) { - memberContextMenuRequested(property, path, globalPos); - }); } return property; @@ -429,54 +428,6 @@ void VariantMapProperty::emitMemberValueChanged(const QStringList &path, const Q emit valueChanged(); } -void VariantMapProperty::memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos) -{ - QMenu menu; - - // Add Expand All and Collapse All actions to group properties - if (auto groupProperty = qobject_cast(property)) { - menu.addAction(tr("Expand All"), groupProperty, &GroupProperty::expandAll); - menu.addAction(tr("Collapse All"), groupProperty, &GroupProperty::collapseAll); - } - - // Provide the Add, Remove and Reset actions also here - if (isEnabled() && property->actions()) { - menu.addSeparator(); - - if (property->actions() & Property::Action::Add) { - QAction *add = menu.addAction(m_addIcon, tr("Add Property"), this, [this, name = path.first()] { - addMember(name, mSuggestions.value(name)); - }); - Utils::setThemeIcon(add, "add"); - } - if (property->actions() & Property::Action::Remove) { - QAction *remove = menu.addAction(m_removeIcon, tr("Remove Property"), this, [this, name = path.first()] { - removeMember(name); - }); - Utils::setThemeIcon(remove, "remove"); - - // If a property can be removed, it can also be renamed - menu.addAction(m_renameIcon, tr("Rename Property..."), this, [this, name = path.first()] { - emit renameRequested(name); - }); - } - if (property->actions() & Property::Action::Reset) { - QAction *reset = menu.addAction(m_resetIcon, tr("Reset Member"), this, [=] { - setClassMember(path, QVariant()); - emitValueChangedRecursively(property); - }); - reset->setEnabled(property->isModified()); - Utils::setThemeIcon(reset, "edit-clear"); - } - } - - // todo: Add "Convert" sub-menu - // todo: Add "Copy" and "Paste" actions - - if (!menu.isEmpty()) - menu.exec(globalPos); -} - AddValueProperty::AddValueProperty(QObject *parent) : Property(QString(), parent) @@ -533,7 +484,7 @@ QWidget *AddValueProperty::createLabel(int level, QWidget *parent) nameEdit->installEventFilter(this); - connect(qApp, &QApplication::focusChanged, nameEdit, [=] (QWidget *, QWidget *focusWidget) { + connect(qApp, &QApplication::focusChanged, this, [=](QWidget *, QWidget *focusWidget) { // Ignore focus in different windows (popups, dialogs, etc.) if (!focusWidget || focusWidget->window() != parent->window()) return; diff --git a/src/tiled/variantmapproperty.h b/src/tiled/variantmapproperty.h index 6c8304f44e..5e66c33181 100644 --- a/src/tiled/variantmapproperty.h +++ b/src/tiled/variantmapproperty.h @@ -52,7 +52,6 @@ class VariantMapProperty : public GroupProperty signals: void memberValueChanged(const QStringList &path, const QVariant &value); - void renameRequested(const QString &name); protected: virtual void propertyTypesChanged(); @@ -79,12 +78,6 @@ class VariantMapProperty : public GroupProperty void emitMemberValueChanged(const QStringList &path, const QVariant &value); - void memberContextMenuRequested(Property *property, const QStringList &path, const QPoint &globalPos); - - QIcon m_resetIcon; - QIcon m_removeIcon; - QIcon m_addIcon; - QIcon m_renameIcon; bool mEmittingValueChanged = false; QVariantMap mValue; QVariantMap mSuggestions; From 754b2a5d7fdfbf76e5b2c634613bb46832ee01ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 11 Dec 2024 17:01:03 +0100 Subject: [PATCH 78/78] Make sure the checkbox label remains readable when selected The QCommonStyle always uses the WindowText role when rendering a checkbox label, so change the palette so that is picks up the HighlightedText role when the property is selected. --- src/tiled/propertiesview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tiled/propertiesview.cpp b/src/tiled/propertiesview.cpp index 0c92c7df86..59b8da8feb 100644 --- a/src/tiled/propertiesview.cpp +++ b/src/tiled/propertiesview.cpp @@ -323,9 +323,16 @@ QWidget *BoolProperty::createEditor(QWidget *parent) auto font = editor->font(); font.setBold(isModified()); editor->setFont(font); + + // Make sure the label remains readable when selected + auto pal = QGuiApplication::palette(); + if (isSelected()) + pal.setBrush(QPalette::WindowText, pal.brush(QPalette::HighlightedText)); + editor->setPalette(pal); }; syncEditor(); + connect(this, &Property::selectedChanged, editor, syncEditor); connect(this, &Property::valueChanged, editor, syncEditor); connect(this, &Property::modifiedChanged, editor, syncEditor); connect(editor, &QCheckBox::toggled, this, [=](bool checked) {