From 3141a391e0f79532b2d61f8b6cd03c98a07a6c0a Mon Sep 17 00:00:00 2001 From: dogboydog Date: Wed, 28 Jun 2023 10:19:01 -0400 Subject: [PATCH] Support custom properties on the project (#3667) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Derive Project from Object, added ProjectDocument and related enum types * Support Project in PropertyBrowser * Extracted PropertiesWidget from PropertiesDock, for reuse in ProjectPropertiesDialog * Derive EditableProject from EditableAsset * Implemented saving and loading of custom project properties * Changed Project to be managed by std::unique_ptr Rather than passing Project around by value and implementing copy/move constructor and assignment operators, the instance is now managed by std::unique_ptr and owned by ProjectDocument, which is in turn owned by ProjectModel. The ProjectDocument now owns the EditableProject, which can be used by scripts and will get invalidated as appropriate when it gets deleted, for example due to unloading the project. Co-authored-by: Thorbjørn Lindeijer --- NEWS.md | 1 + docs/scripting-doc/index.d.ts | 2 +- src/libtiled/logginginterface.cpp | 2 + src/libtiled/object.h | 15 +- src/libtiled/propertytype.cpp | 1 + src/libtiled/propertytype.h | 20 +- src/tiled/automappingmanager.cpp | 2 +- src/tiled/commanddialog.cpp | 2 +- src/tiled/commandmanager.cpp | 2 +- src/tiled/document.h | 3 +- src/tiled/documentmanager.cpp | 1 + src/tiled/editableobject.cpp | 1 + src/tiled/editableproject.cpp | 27 +- src/tiled/editableproject.h | 18 +- src/tiled/exporthelper.cpp | 1 + src/tiled/libtilededitor.qbs | 4 + src/tiled/mainwindow.cpp | 36 +- src/tiled/mainwindow.h | 2 +- src/tiled/project.cpp | 48 ++- src/tiled/project.h | 10 +- src/tiled/projectdocument.cpp | 82 ++++ src/tiled/projectdocument.h | 53 +++ src/tiled/projectmanager.cpp | 9 +- src/tiled/projectmanager.h | 4 +- src/tiled/projectmodel.cpp | 40 +- src/tiled/projectmodel.h | 14 +- src/tiled/projectpropertiesdialog.cpp | 8 +- src/tiled/projectpropertiesdialog.h | 2 + src/tiled/projectpropertiesdialog.ui | 24 +- src/tiled/propertiesdock.cpp | 480 +---------------------- src/tiled/propertiesdock.h | 31 +- src/tiled/propertieswidget.cpp | 531 ++++++++++++++++++++++++++ src/tiled/propertieswidget.h | 83 ++++ src/tiled/propertybrowser.cpp | 8 +- src/tiled/propertytypeseditor.cpp | 4 +- src/tiled/scriptmodule.cpp | 6 +- src/tiled/scriptmodule.h | 6 +- 37 files changed, 983 insertions(+), 600 deletions(-) create mode 100644 src/tiled/projectdocument.cpp create mode 100644 src/tiled/projectdocument.h create mode 100644 src/tiled/propertieswidget.cpp create mode 100644 src/tiled/propertieswidget.h diff --git a/NEWS.md b/NEWS.md index 28104d3e98..0f1c08079c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ ### Unreleased +* Added support for setting custom properties on the project (#2903) * Removed Space and Ctrl+Space shortcuts from Layers view to avoid conflict with panning (#3672) * Scripting: Added API for editing tile layers using terrain sets (with a-morphous, #3758) * Fixed object preview position with parallax factor on group layer (#3669) diff --git a/docs/scripting-doc/index.d.ts b/docs/scripting-doc/index.d.ts index a5f4253a25..ddacea6caf 100644 --- a/docs/scripting-doc/index.d.ts +++ b/docs/scripting-doc/index.d.ts @@ -1091,7 +1091,7 @@ declare class TiledObject { * * @since 1.10.1 */ -declare class Project { +declare class Project extends TiledObject { /** * A project-specific directory where you can put Tiled extensions. * diff --git a/src/libtiled/logginginterface.cpp b/src/libtiled/logginginterface.cpp index 0bb8d9fb4c..b63674112d 100644 --- a/src/libtiled/logginginterface.cpp +++ b/src/libtiled/logginginterface.cpp @@ -187,6 +187,8 @@ SelectCustomProperty::SelectCustomProperty(QString fileName, // not so helpful... would need WangSet index as well id = static_cast(object)->colorIndex(); break; + case Object::ProjectType: + break; } } diff --git a/src/libtiled/object.h b/src/libtiled/object.h index 7a37dbb148..b346e16466 100644 --- a/src/libtiled/object.h +++ b/src/libtiled/object.h @@ -41,13 +41,14 @@ class TILEDSHARED_EXPORT Object public: // Keep values synchronized with ClassPropertyType::ClassUsageFlag enum TypeId { - LayerType = 0x02, - MapObjectType = 0x04, - MapType = 0x08, - TilesetType = 0x10, - TileType = 0x20, - WangSetType = 0x40, - WangColorType = 0x80, + LayerType = 0x002, + MapObjectType = 0x004, + MapType = 0x008, + TilesetType = 0x010, + TileType = 0x020, + WangSetType = 0x040, + WangColorType = 0x080, + ProjectType = 0x100, }; explicit Object(TypeId typeId, const QString &className = QString()) diff --git a/src/libtiled/propertytype.cpp b/src/libtiled/propertytype.cpp index 3f219d33cb..5200877d16 100644 --- a/src/libtiled/propertytype.cpp +++ b/src/libtiled/propertytype.cpp @@ -308,6 +308,7 @@ static const struct { { ClassPropertyType::TilesetClass, QLatin1String("tileset") }, { ClassPropertyType::WangColorClass, QLatin1String("wangcolor") }, { ClassPropertyType::WangSetClass, QLatin1String("wangset") }, + { ClassPropertyType::ProjectClass, QLatin1String("project") }, }; QJsonObject ClassPropertyType::toJson(const ExportContext &context) const diff --git a/src/libtiled/propertytype.h b/src/libtiled/propertytype.h index 1959a071d5..99969713fa 100644 --- a/src/libtiled/propertytype.h +++ b/src/libtiled/propertytype.h @@ -135,18 +135,18 @@ class TILEDSHARED_EXPORT ClassPropertyType final : public PropertyType { public: enum ClassUsageFlag { - PropertyValueType = 0x01, + PropertyValueType = 0x001, // Keep values synchronized with Object::TypeId - LayerClass = 0x02, - MapObjectClass = 0x04, - MapClass = 0x08, - TilesetClass = 0x10, - TileClass = 0x20, - WangSetClass = 0x40, - WangColorClass = 0x80, - - AnyUsage = 0xFF, + LayerClass = 0x002, + MapObjectClass = 0x004, + MapClass = 0x008, + TilesetClass = 0x010, + TileClass = 0x020, + WangSetClass = 0x040, + WangColorClass = 0x080, + ProjectClass = 0x100, + AnyUsage = 0xFFF, AnyObjectClass = AnyUsage & ~PropertyValueType, }; diff --git a/src/tiled/automappingmanager.cpp b/src/tiled/automappingmanager.cpp index f36078f838..b84070d0f4 100644 --- a/src/tiled/automappingmanager.cpp +++ b/src/tiled/automappingmanager.cpp @@ -325,7 +325,7 @@ void AutomappingManager::refreshRulesFile(const QString &ruleFileOverride) } if (rulesFile.isEmpty() || !QFileInfo::exists(rulesFile)) { - auto &project = ProjectManager::instance()->project(); + const auto &project = ProjectManager::instance()->project(); if (!project.mAutomappingRulesFile.isEmpty()) rulesFile = project.mAutomappingRulesFile; } diff --git a/src/tiled/commanddialog.cpp b/src/tiled/commanddialog.cpp index 9b94f829a5..1e2d56e4a7 100644 --- a/src/tiled/commanddialog.cpp +++ b/src/tiled/commanddialog.cpp @@ -51,7 +51,7 @@ CommandDialog::CommandDialog(QWidget *parent) mUi->tabWidget->addTab(mGlobalCommandsEdit, tr("Global Commands")); mUi->tabWidget->addTab(mProjectCommandsEdit, tr("Project Commands")); - auto &project = ProjectManager::instance()->project(); + const auto &project = ProjectManager::instance()->project(); mUi->tabWidget->setTabEnabled(1, !project.fileName().isEmpty()); Utils::restoreGeometry(this); diff --git a/src/tiled/commandmanager.cpp b/src/tiled/commandmanager.cpp index 9f43bd8e14..3625a171e0 100644 --- a/src/tiled/commandmanager.cpp +++ b/src/tiled/commandmanager.cpp @@ -109,7 +109,7 @@ const QVector &CommandManager::globalCommands() const const QVector &CommandManager::projectCommands() const { - auto &project = ProjectManager::instance()->project(); + const auto &project = ProjectManager::instance()->project(); return project.mCommands; } diff --git a/src/tiled/document.h b/src/tiled/document.h index 7e7b20fa97..5adfd9930c 100644 --- a/src/tiled/document.h +++ b/src/tiled/document.h @@ -56,7 +56,8 @@ class Document : public QObject, enum DocumentType { MapDocumentType, TilesetDocumentType, - WorldDocumentType + WorldDocumentType, + ProjectDocumentType }; Document(DocumentType type, diff --git a/src/tiled/documentmanager.cpp b/src/tiled/documentmanager.cpp index c6c62edd16..f8f30f9af3 100644 --- a/src/tiled/documentmanager.cpp +++ b/src/tiled/documentmanager.cpp @@ -243,6 +243,7 @@ DocumentManager::DocumentManager(QObject *parent) break; } case Document::WorldDocumentType: + case Document::ProjectDocumentType: break; } diff --git a/src/tiled/editableobject.cpp b/src/tiled/editableobject.cpp index edd1c9f5ea..b9b5fc5795 100644 --- a/src/tiled/editableobject.cpp +++ b/src/tiled/editableobject.cpp @@ -112,6 +112,7 @@ static Map *mapForObject(Object *object) case Object::TileType: case Object::WangSetType: case Object::WangColorType: + case Object::ProjectType: break; } return nullptr; diff --git a/src/tiled/editableproject.cpp b/src/tiled/editableproject.cpp index 832568ce35..77033d5d8a 100644 --- a/src/tiled/editableproject.cpp +++ b/src/tiled/editableproject.cpp @@ -21,32 +21,45 @@ #include "editableproject.h" +#include "projectdocument.h" + namespace Tiled { -EditableProject::EditableProject(Project *project, QObject *parent) - : QObject(parent) - , mProject(project) +EditableProject::EditableProject(ProjectDocument *projectDocument, QObject *parent) + : EditableAsset(projectDocument, &projectDocument->project(), parent) { } QString EditableProject::extensionsPath() const { - return mProject->mExtensionsPath; + return project()->mExtensionsPath; } QString EditableProject::automappingRulesFile() const { - return mProject->mAutomappingRulesFile; + return project()->mAutomappingRulesFile; } QString EditableProject::fileName() const { - return mProject->fileName(); + return project()->fileName(); } QStringList EditableProject::folders() const { - return mProject->folders(); + return project()->folders(); +} + +bool EditableProject::isReadOnly() const +{ + return false; +} + +QSharedPointer EditableProject::createDocument() +{ + // We don't currently support opening a project in a tab, which this + // function is meant for. + return nullptr; } } // namespace Tiled diff --git a/src/tiled/editableproject.h b/src/tiled/editableproject.h index 781e88a938..2006370ed8 100644 --- a/src/tiled/editableproject.h +++ b/src/tiled/editableproject.h @@ -21,13 +21,16 @@ #pragma once +#include "editableasset.h" #include "project.h" #include namespace Tiled { -class EditableProject : public QObject +class ProjectDocument; + +class EditableProject final : public EditableAsset { Q_OBJECT @@ -37,17 +40,24 @@ class EditableProject : public QObject Q_PROPERTY(QStringList folders READ folders) public: - EditableProject(Project *project, QObject *parent = nullptr); + EditableProject(ProjectDocument *projectDocument, QObject *parent = nullptr); + bool isReadOnly() const override; QString extensionsPath() const; QString automappingRulesFile() const; QString fileName() const; QStringList folders() const; -private: - Project *mProject; + Project *project() const; + + QSharedPointer createDocument() override; }; +inline Project *EditableProject::project() const +{ + return static_cast(object()); +} + } // namespace Tiled Q_DECLARE_METATYPE(Tiled::EditableProject*) diff --git a/src/tiled/exporthelper.cpp b/src/tiled/exporthelper.cpp index 1c1a153e29..69597c60ba 100644 --- a/src/tiled/exporthelper.cpp +++ b/src/tiled/exporthelper.cpp @@ -206,6 +206,7 @@ void ExportHelper::resolveProperties(Object *object) const resolveProperties(color.data()); break; case Object::WangColorType: + case Object::ProjectType: break; } diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index f6021fa756..91e3149177 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -390,6 +390,8 @@ DynamicLibrary { "project.h", "projectdock.cpp", "projectdock.h", + "projectdocument.cpp", + "projectdocument.h", "projectmanager.cpp", "projectmanager.h", "projectmodel.cpp", @@ -399,6 +401,8 @@ DynamicLibrary { "projectpropertiesdialog.ui", "propertiesdock.cpp", "propertiesdock.h", + "propertieswidget.cpp", + "propertieswidget.h", "propertybrowser.cpp", "propertybrowser.h", "propertytypeseditor.cpp", diff --git a/src/tiled/mainwindow.cpp b/src/tiled/mainwindow.cpp index 22d3b2bd19..5bd46c38b1 100644 --- a/src/tiled/mainwindow.cpp +++ b/src/tiled/mainwindow.cpp @@ -56,6 +56,7 @@ #include "newtilesetdialog.h" #include "offsetmapdialog.h" #include "projectdock.h" +#include "projectdocument.h" #include "projectmanager.h" #include "projectpropertiesdialog.h" #include "propertytypeseditor.h" @@ -991,8 +992,11 @@ void MainWindow::initializeSession() const auto &session = Session::current(); // Restore associated project if applicable - Project project; - bool projectLoaded = !session.project.isEmpty() && project.load(session.project); + std::unique_ptr project; + if (!session.project.isEmpty()) + project = Project::load(session.project); + + const bool projectLoaded = project != nullptr; if (projectLoaded) { ProjectManager::instance()->setProject(std::move(project)); @@ -1379,9 +1383,9 @@ bool MainWindow::closeAllFiles() bool MainWindow::openProjectFile(const QString &fileName) { - Project project; + auto project = Project::load(fileName); - if (!project.load(fileName)) { + if (!project) { QMessageBox::critical(window(), tr("Error Opening Project"), tr("An error occurred while opening the project.")); @@ -1413,10 +1417,10 @@ void MainWindow::newProject() fileName.append(QStringLiteral(".tiled-project")); } - Project project; - project.addFolder(QFileInfo(fileName).path()); + auto project = std::make_unique(); + project->addFolder(QFileInfo(fileName).path()); - if (!project.save(fileName)) { + if (!project->save(fileName)) { QMessageBox::critical(window(), tr("Error Saving Project"), tr("An error occurred while saving the project.")); @@ -1435,10 +1439,10 @@ bool MainWindow::closeProject() if (project.fileName().isEmpty()) return true; - return switchProject(Project{}); + return switchProject(nullptr); } -bool MainWindow::switchProject(Project project) +bool MainWindow::switchProject(std::unique_ptr project) { auto prefs = Preferences::instance(); emit prefs->aboutToSwitchSession(); @@ -1448,11 +1452,15 @@ bool MainWindow::switchProject(Project project) WorldManager::instance().unloadAllWorlds(); - auto &session = Session::switchCurrent(Session::defaultFileNameForProject(project.fileName())); + if (project) { + auto &session = Session::switchCurrent(Session::defaultFileNameForProject(project->fileName())); - if (!project.fileName().isEmpty()) { - session.setProject(project.fileName()); - prefs->addRecentProject(project.fileName()); + if (!project->fileName().isEmpty()) { + session.setProject(project->fileName()); + prefs->addRecentProject(project->fileName()); + } + } else { + Session::switchCurrent(Session::defaultFileName()); } ProjectManager::instance()->setProject(std::move(project)); @@ -1487,6 +1495,8 @@ void MainWindow::restoreSession() void MainWindow::projectProperties() { Project &project = ProjectManager::instance()->project(); + if (project.fileName().length() == 0) + return; if (ProjectPropertiesDialog(project, this).exec() == QDialog::Accepted) { project.save(); diff --git a/src/tiled/mainwindow.h b/src/tiled/mainwindow.h index 36c5c105f2..cfea95ee81 100644 --- a/src/tiled/mainwindow.h +++ b/src/tiled/mainwindow.h @@ -131,7 +131,7 @@ class TILED_EDITOR_EXPORT MainWindow : public QMainWindow bool openProjectFile(const QString &fileName); void newProject(); bool closeProject(); - bool switchProject(Project project); + bool switchProject(std::unique_ptr project); void restoreSession(); void projectProperties(); diff --git a/src/tiled/project.cpp b/src/tiled/project.cpp index 45ae98ffad..82907ab83b 100644 --- a/src/tiled/project.cpp +++ b/src/tiled/project.cpp @@ -19,7 +19,6 @@ */ #include "project.h" -#include "preferences.h" #include "properties.h" #include "savefile.h" @@ -45,7 +44,8 @@ static QString absolute(const QDir &dir, const QString &fileName) } Project::Project() - : mPropertyTypes(SharedPropertyTypes::create()) + : Object(Object::ProjectType) + , mPropertyTypes(SharedPropertyTypes::create()) { } @@ -75,13 +75,15 @@ bool Project::save(const QString &fileName) commands.append(QJsonObject::fromVariantHash(command.toVariant())); const QJsonArray propertyTypes = mPropertyTypes->toJson(dir.path()); - + const ExportContext context(*mPropertyTypes, dir.path()); + const QJsonArray projectProperties = propertiesToJson(properties(), context); QJsonObject project { { QStringLiteral("propertyTypes"), propertyTypes }, { QStringLiteral("folders"), folders }, { QStringLiteral("extensionsPath"), relative(dir, extensionsPath) }, { QStringLiteral("automappingRulesFile"), dir.relativeFilePath(mAutomappingRulesFile) }, { QStringLiteral("commands"), commands }, + { QStringLiteral("properties"), projectProperties }, }; if (mCompatibilityVersion != Tiled_Latest) @@ -103,42 +105,48 @@ bool Project::save(const QString &fileName) return true; } -bool Project::load(const QString &fileName) +std::unique_ptr Project::load(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - return false; + return nullptr; QJsonParseError error; const QByteArray json = file.readAll(); const QJsonDocument document(QJsonDocument::fromJson(json, &error)); if (error.error != QJsonParseError::NoError) - return false; + return nullptr; - mFileName = fileName; + auto project = std::make_unique(); + project->mFileName = fileName; const QDir dir = QFileInfo(fileName).dir(); - const QJsonObject project = document.object(); + const QJsonObject projectJson = document.object(); + + project->mExtensionsPath = absolute(dir, projectJson.value(QLatin1String("extensionsPath")).toString(QStringLiteral("extensions"))); + project->mObjectTypesFile = absolute(dir, projectJson.value(QLatin1String("objectTypesFile")).toString()); + project->mAutomappingRulesFile = absolute(dir, projectJson.value(QLatin1String("automappingRulesFile")).toString()); - mExtensionsPath = absolute(dir, project.value(QLatin1String("extensionsPath")).toString(QStringLiteral("extensions"))); - mObjectTypesFile = absolute(dir, project.value(QLatin1String("objectTypesFile")).toString()); - mAutomappingRulesFile = absolute(dir, project.value(QLatin1String("automappingRulesFile")).toString()); + project->mPropertyTypes->loadFromJson(projectJson.value(QLatin1String("propertyTypes")).toArray(), dir.path()); - mPropertyTypes->loadFromJson(project.value(QLatin1String("propertyTypes")).toArray(), dir.path()); + const QString projectPropertiesKey = QLatin1String("properties"); + if (projectJson.contains(projectPropertiesKey)) { + const ExportContext context(*project->mPropertyTypes, dir.path()); + const Properties loadedProperties = propertiesFromJson(projectJson.value(projectPropertiesKey).toArray(), context); + project->setProperties(loadedProperties); + } - mFolders.clear(); - const QJsonArray folders = project.value(QLatin1String("folders")).toArray(); + const QJsonArray folders = projectJson.value(QLatin1String("folders")).toArray(); for (const QJsonValue &folderValue : folders) - mFolders.append(QDir::cleanPath(dir.absoluteFilePath(folderValue.toString()))); + project->mFolders.append(QDir::cleanPath(dir.absoluteFilePath(folderValue.toString()))); - mCommands.clear(); - const QJsonArray commands = project.value(QLatin1String("commands")).toArray(); + const QJsonArray commands = projectJson.value(QLatin1String("commands")).toArray(); for (const QJsonValue &commandValue : commands) - mCommands.append(Command::fromVariant(commandValue.toVariant())); + project->mCommands.append(Command::fromVariant(commandValue.toVariant())); - mCompatibilityVersion = static_cast(project.value(QLatin1String("compatibilityVersion")).toInt(Tiled_Latest)); + project->mCompatibilityVersion = static_cast(projectJson.value(QLatin1String("compatibilityVersion")).toInt(Tiled_Latest)); - return true; + return project; } void Project::addFolder(const QString &folder) diff --git a/src/tiled/project.h b/src/tiled/project.h index 0756c1dc87..172e52e301 100644 --- a/src/tiled/project.h +++ b/src/tiled/project.h @@ -21,17 +21,18 @@ #pragma once #include "command.h" -#include "properties.h" -#include "propertytype.h" +#include "object.h" #include "tiled.h" #include #include #include +#include + namespace Tiled { -class Project +class Project : public Object { public: Project(); @@ -39,7 +40,8 @@ class Project const QString &fileName() const; bool save(); bool save(const QString &fileName); - bool load(const QString &fileName); + + static std::unique_ptr load(const QString &fileName); void addFolder(const QString &folder); void removeFolder(int index); diff --git a/src/tiled/projectdocument.cpp b/src/tiled/projectdocument.cpp new file mode 100644 index 0000000000..26849d85f4 --- /dev/null +++ b/src/tiled/projectdocument.cpp @@ -0,0 +1,82 @@ +/* + * projectdocument.cpp + * Copyright 2023, Chris Boehm AKA dogboydog + * Copyright 2023, 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 "projectdocument.h" + +#include "editableproject.h" + +#include + +namespace Tiled { + +ProjectDocument::ProjectDocument(std::unique_ptr project, QObject *parent) + : Document(ProjectDocumentType, project->fileName(), parent) +{ + mProject = std::move(project); + mCurrentObject = mProject.get(); + + connect(undoStack(), &QUndoStack::indexChanged, + this, [this] { mProject->save(); }); +} + +QString ProjectDocument::displayName() const +{ + return mProject->fileName(); +} + +bool ProjectDocument::save(const QString &/* fileName */, QString */* error */) +{ + return mProject->save(); +} + +FileFormat *ProjectDocument::writerFormat() const +{ + return nullptr; +} + +void ProjectDocument::setExportFormat(FileFormat *) +{ + // do nothing +} + +FileFormat *ProjectDocument::exportFormat() const +{ + return nullptr; +} + +QString ProjectDocument::lastExportFileName() const +{ + return mProject->fileName(); +} + +void ProjectDocument::setLastExportFileName(const QString &/* fileName */) +{ + // do nothing +} + +std::unique_ptr ProjectDocument::createEditable() +{ + return std::make_unique(this, this); +} + +} // namespace Tiled + +#include "moc_projectdocument.cpp" diff --git a/src/tiled/projectdocument.h b/src/tiled/projectdocument.h new file mode 100644 index 0000000000..310bd83c22 --- /dev/null +++ b/src/tiled/projectdocument.h @@ -0,0 +1,53 @@ +/* + * projectdocument.h + * Copyright 2023, Chris Boehm AKA dogboydog + * Copyright 2023, 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 "document.h" +#include "project.h" + +namespace Tiled { + +class ProjectDocument : public Document +{ + Q_OBJECT + +public: + ProjectDocument(std::unique_ptr project, QObject *parent = nullptr); + + QString displayName() const override; + FileFormat *writerFormat() const override; + bool save(const QString &fileName, QString *error) override; + void setExportFormat(FileFormat *format) override; + FileFormat *exportFormat() const override; + QString lastExportFileName() const override; + void setLastExportFileName(const QString &fileName) override; + std::unique_ptr createEditable() override; + + Project &project() { return *mProject; } + +private: + std::unique_ptr mProject; +}; + +using ProjectDocumentPtr = QSharedPointer; + +} // namespace Tiled diff --git a/src/tiled/projectmanager.cpp b/src/tiled/projectmanager.cpp index 14a4e111d5..d7ff950be0 100644 --- a/src/tiled/projectmanager.cpp +++ b/src/tiled/projectmanager.cpp @@ -40,11 +40,11 @@ ProjectManager::ProjectManager(QObject *parent) /** * Replaces the current project with the given \a project. */ -void ProjectManager::setProject(Project _project) +void ProjectManager::setProject(std::unique_ptr _project) { mProjectModel->setProject(std::move(_project)); - auto &project = mProjectModel->project(); // _project was moved + auto &project = mProjectModel->project(); // Automatically import object types if they are referenced by the project if (!project.mObjectTypesFile.isEmpty()) { @@ -71,6 +71,11 @@ Project &ProjectManager::project() return mProjectModel->project(); } +EditableAsset *ProjectManager::editableProject() +{ + return mProjectModel->editableProject(); +} + } // namespace Tiled #include "moc_projectmanager.cpp" diff --git a/src/tiled/projectmanager.h b/src/tiled/projectmanager.h index 5704b86bf1..50af4707ad 100644 --- a/src/tiled/projectmanager.h +++ b/src/tiled/projectmanager.h @@ -26,6 +26,7 @@ namespace Tiled { +class EditableAsset; class ProjectModel; /** @@ -42,8 +43,9 @@ class ProjectManager : public QObject static ProjectManager *instance(); - void setProject(Project project); + void setProject(std::unique_ptr project); Project &project(); + EditableAsset *editableProject(); ProjectModel *projectModel(); diff --git a/src/tiled/projectmodel.cpp b/src/tiled/projectmodel.cpp index e9b63fb304..ae74058bbc 100644 --- a/src/tiled/projectmodel.cpp +++ b/src/tiled/projectmodel.cpp @@ -141,37 +141,57 @@ ProjectModel::~ProjectModel() mScanningThread.wait(); } -void ProjectModel::setProject(Project project) +void ProjectModel::setProject(std::unique_ptr project) { if (mUpdateNameFiltersTimer.isActive()) updateNameFilters(); beginResetModel(); - mProject = std::move(project); + if (project) + mProjectDocument = std::make_unique(std::move(project)); + else + mProjectDocument.reset(); + mFolders.clear(); mFoldersPendingScan.clear(); - for (const QString &folder : mProject.folders()) { + const auto &folders = this->project().folders(); + for (const QString &folder : folders) { mFolders.push_back(std::make_unique(folder)); scheduleFolderScan(folder); } mWatcher.clear(); - mWatcher.addPaths(mProject.folders()); + mWatcher.addPaths(folders); endResetModel(); } +Project &ProjectModel::project() +{ + return mProjectDocument ? mProjectDocument->project() : mEmptyProject; +} + +EditableAsset *ProjectModel::editableProject() +{ + return mProjectDocument ? mProjectDocument->editable() : nullptr; +} + void ProjectModel::addFolder(const QString &folder) { - const int row = int(mProject.folders().size()); + if (!mProjectDocument) + return; + + const int row = int(project().folders().size()); beginInsertRows(QModelIndex(), row, row); - mProject.addFolder(folder); + project().addFolder(folder); + mFolders.push_back(std::make_unique(folder)); mWatcher.addPath(folder); + scheduleFolderScan(folder); endInsertRows(); @@ -181,6 +201,9 @@ void ProjectModel::addFolder(const QString &folder) void ProjectModel::removeFolder(int row) { + if (!mProjectDocument) + return; + const QString folder = mFolders.at(row)->filePath; QStringList watchedFilePaths; @@ -188,9 +211,12 @@ void ProjectModel::removeFolder(int row) collectDirectories(*mFolders.at(row), watchedFilePaths); beginRemoveRows(QModelIndex(), row, row); - mProject.removeFolder(row); + + project().removeFolder(row); + mFolders.erase(mFolders.begin() + row); mWatcher.removePaths(watchedFilePaths); + endRemoveRows(); emit folderRemoved(folder); diff --git a/src/tiled/projectmodel.h b/src/tiled/projectmodel.h index 94396cf4e2..dce87d465d 100644 --- a/src/tiled/projectmodel.h +++ b/src/tiled/projectmodel.h @@ -21,7 +21,7 @@ #pragma once #include "filesystemwatcher.h" -#include "project.h" +#include "projectdocument.h" #include #include @@ -56,8 +56,9 @@ class ProjectModel : public QAbstractItemModel void updateNameFilters(); - void setProject(Project project); + void setProject(std::unique_ptr project); Project &project(); + EditableAsset *editableProject(); void addFolder(const QString &folder); void removeFolder(int row); @@ -114,7 +115,8 @@ class ProjectModel : public QAbstractItemModel void scheduleFolderScan(const QString &folder); void folderScanned(FolderEntry *entry); - Project mProject; + std::unique_ptr mProjectDocument; + Project mEmptyProject; QFileIconProvider mFileIconProvider; QStringList mNameFilters; QTimer mUpdateNameFiltersTimer; @@ -127,10 +129,4 @@ class ProjectModel : public QAbstractItemModel FileSystemWatcher mWatcher; }; - -inline Project &ProjectModel::project() -{ - return mProject; -} - } // namespace Tiled diff --git a/src/tiled/projectpropertiesdialog.cpp b/src/tiled/projectpropertiesdialog.cpp index f3f3f94e96..304abb8d85 100644 --- a/src/tiled/projectpropertiesdialog.cpp +++ b/src/tiled/projectpropertiesdialog.cpp @@ -23,7 +23,7 @@ #include "mapformat.h" #include "project.h" -#include "utils.h" +#include "projectdocument.h" #include "varianteditorfactory.h" #include "variantpropertymanager.h" @@ -35,9 +35,12 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project &project, QWidget *pare : QDialog(parent) , ui(new Ui::ProjectPropertiesDialog) , mProject(project) + , mPropertiesProjectDocument(new ProjectDocument(std::make_unique(), this)) { ui->setupUi(this); + mPropertiesProjectDocument->project().setProperties(project.properties()); + auto variantPropertyManager = new VariantPropertyManager(this); auto variantEditorFactory = new VariantEditorFactory(this); auto groupPropertyManager = new QtGroupPropertyManager(this); @@ -79,6 +82,8 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project &project, QWidget *pare ui->propertyBrowser->addProperty(generalGroupProperty); ui->propertyBrowser->addProperty(filesGroupProperty); + + ui->propertiesWidget->setDocument(mPropertiesProjectDocument); } ProjectPropertiesDialog::~ProjectPropertiesDialog() @@ -88,6 +93,7 @@ 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(); diff --git a/src/tiled/projectpropertiesdialog.h b/src/tiled/projectpropertiesdialog.h index 3c033c8f18..dbfd4e59d9 100644 --- a/src/tiled/projectpropertiesdialog.h +++ b/src/tiled/projectpropertiesdialog.h @@ -33,6 +33,7 @@ class ProjectPropertiesDialog; namespace Tiled { class Project; +class ProjectDocument; class ProjectPropertiesDialog : public QDialog { @@ -48,6 +49,7 @@ class ProjectPropertiesDialog : public QDialog Ui::ProjectPropertiesDialog *ui; Project &mProject; + ProjectDocument *mPropertiesProjectDocument; QList mVersions; QtVariantProperty *mCompatibilityVersionProperty; QtVariantProperty *mExtensionPathProperty; diff --git a/src/tiled/projectpropertiesdialog.ui b/src/tiled/projectpropertiesdialog.ui index 5194942dc4..899db3ac9c 100644 --- a/src/tiled/projectpropertiesdialog.ui +++ b/src/tiled/projectpropertiesdialog.ui @@ -6,8 +6,8 @@ 0 0 - 575 - 168 + 586 + 356 @@ -17,6 +17,21 @@ + + + + + 0 + 1 + + + + + + + + + @@ -36,6 +51,11 @@
QtGroupBoxPropertyBrowser
1 + + Tiled::PropertiesWidget + QTreeView +
propertieswidget.h
+
diff --git a/src/tiled/propertiesdock.cpp b/src/tiled/propertiesdock.cpp index 488312f1cb..36cde21f64 100644 --- a/src/tiled/propertiesdock.cpp +++ b/src/tiled/propertiesdock.cpp @@ -20,481 +20,39 @@ #include "propertiesdock.h" -#include "actionmanager.h" -#include "addpropertydialog.h" -#include "changeproperties.h" -#include "clipboardmanager.h" -#include "documentmanager.h" -#include "mapdocument.h" -#include "mapobject.h" -#include "propertybrowser.h" -#include "tile.h" -#include "tileset.h" -#include "utils.h" -#include "variantpropertymanager.h" +#include "propertieswidget.h" -#include #include -#include -#include -#include -#include -#include -#include -#include namespace Tiled { PropertiesDock::PropertiesDock(QWidget *parent) : QDockWidget(parent) - , mDocument(nullptr) - , mPropertyBrowser(new PropertyBrowser) + , mPropertiesWidget(new PropertiesWidget(this)) { setObjectName(QLatin1String("propertiesDock")); + setWidget(mPropertiesWidget); - mActionAddProperty = new QAction(this); - mActionAddProperty->setEnabled(false); - mActionAddProperty->setIcon(QIcon(QLatin1String(":/images/16/add.png"))); - connect(mActionAddProperty, &QAction::triggered, - this, &PropertiesDock::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, &PropertiesDock::removeProperties); - - mActionRenameProperty = new QAction(this); - mActionRenameProperty->setEnabled(false); - mActionRenameProperty->setIcon(QIcon(QLatin1String(":/images/16/rename.png"))); - connect(mActionRenameProperty, &QAction::triggered, - this, &PropertiesDock::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); - - QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(mPropertyBrowser); - layout->addWidget(toolBar); - widget->setLayout(layout); - - setWidget(widget); - - mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); - connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, - this, &PropertiesDock::showContextMenu); - connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, - this, &PropertiesDock::updateActions); + connect(mPropertiesWidget, &PropertiesWidget::bringToFront, + this, &PropertiesDock::bringToFront); retranslateUi(); } void PropertiesDock::setDocument(Document *document) { - if (mDocument == document) - return; - - if (mDocument) - mDocument->disconnect(this); - - mDocument = document; - mPropertyBrowser->setDocument(document); - - if (document) { - connect(document, &Document::currentObjectChanged, - this, &PropertiesDock::currentObjectChanged); - connect(document, &Document::editCurrentObject, - this, &PropertiesDock::bringToFront); - - connect(document, &Document::propertyAdded, - this, &PropertiesDock::updateActions); - connect(document, &Document::propertyRemoved, - this, &PropertiesDock::updateActions); - - currentObjectChanged(document->currentObject()); - } else { - currentObjectChanged(nullptr); - } -} - -void PropertiesDock::bringToFront() -{ - show(); - raise(); - mPropertyBrowser->setFocus(); + mPropertiesWidget->setDocument(document); } void PropertiesDock::selectCustomProperty(const QString &name) { bringToFront(); - mPropertyBrowser->selectCustomProperty(name); -} - -static bool anyObjectHasProperty(const QList &objects, const QString &name) -{ - for (Object *obj : objects) { - if (obj->hasProperty(name)) - return true; - } - return false; -} - -void PropertiesDock::currentObjectChanged(Object *object) -{ - mPropertyBrowser->setObject(object); - - bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; - bool isTileset = object && object->isPartOfTileset(); - bool enabled = object && (!isTileset || editingTileset); - - mPropertyBrowser->setEnabled(object); - mActionAddProperty->setEnabled(enabled); -} - -void PropertiesDock::updateActions() -{ - const QList items = mPropertyBrowser->selectedItems(); - bool allCustomProperties = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); - bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; - bool isTileset = mPropertyBrowser->object() && mPropertyBrowser->object()->isPartOfTileset(); - bool canModify = allCustomProperties && (!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())) { - canModify = false; - break; - } - } - } - - mActionRemoveProperty->setEnabled(canModify); - mActionRenameProperty->setEnabled(canModify && items.size() == 1); -} - -void PropertiesDock::cutProperties() -{ - if (copyProperties()) - removeProperties(); -} - -bool PropertiesDock::copyProperties() -{ - Object *object = mPropertyBrowser->object(); - 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 QVariant value = object->property(name); - if (!value.isValid()) - return false; - - properties.insert(name, value); - } - - ClipboardManager::instance()->setProperties(properties); - return true; -} - -void PropertiesDock::pasteProperties() -{ - auto clipboardManager = ClipboardManager::instance(); - - Properties pastedProperties = clipboardManager->properties(); - if (pastedProperties.isEmpty()) - return; - - const QList objects = mDocument->currentObjects(); - if (objects.isEmpty()) - return; - - QList commands; - - for (Object *object : objects) { - Properties properties = object->properties(); - mergeProperties(properties, pastedProperties); - - if (object->properties() != properties) { - commands.append(new ChangeProperties(mDocument, QString(), object, - properties)); - } - } - - if (!commands.isEmpty()) { - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->beginMacro(tr("Paste Property/Properties", nullptr, - pastedProperties.size())); - - for (QUndoCommand *command : commands) - undoStack->push(command); - - undoStack->endMacro(); - } -} - -void PropertiesDock::openAddPropertyDialog() -{ - AddPropertyDialog dialog(mPropertyBrowser); - if (dialog.exec() == AddPropertyDialog::Accepted) - addProperty(dialog.propertyName(), dialog.propertyValue()); -} - -void PropertiesDock::addProperty(const QString &name, const QVariant &value) -{ - if (name.isEmpty()) - return; - Object *object = mDocument->currentObject(); - if (!object) - return; - - if (!object->hasProperty(name)) { - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new SetProperty(mDocument, - mDocument->currentObjects(), - name, value)); - } - - mPropertyBrowser->editCustomProperty(name); -} - -void PropertiesDock::removeProperties() -{ - Object *object = mDocument->currentObject(); - if (!object) - return; - - const QList items = mPropertyBrowser->selectedItems(); - if (items.isEmpty() || !mPropertyBrowser->allCustomPropertyItems(items)) - return; - - QStringList propertyNames; - for (QtBrowserItem *item : items) - propertyNames.append(item->property()->propertyName()); - - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->beginMacro(tr("Remove Property/Properties", nullptr, - propertyNames.size())); - - for (const QString &name : propertyNames) { - undoStack->push(new RemoveProperty(mDocument, - mDocument->currentObjects(), - name)); - } - - undoStack->endMacro(); -} - -void PropertiesDock::renameProperty() -{ - 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(tr("Name:")); - dialog->setTextValue(oldName); - dialog->setWindowTitle(tr("Rename Property")); - connect(dialog, &QInputDialog::textValueSelected, this, &PropertiesDock::renamePropertyTo); - dialog->open(); -} - -void PropertiesDock::renamePropertyTo(const QString &name) -{ - if (name.isEmpty()) - return; - - QtBrowserItem *item = mPropertyBrowser->currentItem(); - if (!item) - return; - - const QString oldName = item->property()->propertyName(); - if (oldName == name) - return; - - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->push(new RenameProperty(mDocument, mDocument->currentObjects(), oldName, name)); -} - -void PropertiesDock::showContextMenu(const QPoint &pos) -{ - const Object *object = mDocument->currentObject(); - if (!object) - return; - - const QList items = mPropertyBrowser->selectedItems(); - const bool customPropertiesSelected = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); - - bool currentObjectHasAllProperties = true; - QStringList propertyNames; - for (QtBrowserItem *item : items) { - const QString propertyName = item->property()->propertyName(); - propertyNames.append(propertyName); - - if (!object->hasProperty(propertyName)) - currentObjectHasAllProperties = false; - } - - QMenu contextMenu(mPropertyBrowser); - - 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(); - - if (!localFile.isEmpty()) { - Utils::addOpenContainingFolderAction(contextMenu, localFile); - - if (QFileInfo { localFile }.isFile()) - Utils::addOpenWithSystemEditorAction(contextMenu, localFile); - } - } else if (value.userType() == objectRefTypeId()) { - if (auto mapDocument = qobject_cast(mDocument)) { - const DisplayObjectRef objectRef(value.value(), mapDocument); - - contextMenu.addAction(tr("Go to Object"), [=] { - if (auto object = objectRef.object()) { - objectRef.mapDocument->setSelectedObjects({object}); - emit objectRef.mapDocument->focusMapObjectRequested(object); - } - })->setEnabled(objectRef.object()); - } - } - } - - if (!contextMenu.isEmpty()) - contextMenu.addSeparator(); - - QAction *cutAction = contextMenu.addAction(tr("Cu&t"), this, &PropertiesDock::cutProperties); - QAction *copyAction = contextMenu.addAction(tr("&Copy"), this, &PropertiesDock::copyProperties); - QAction *pasteAction = contextMenu.addAction(tr("&Paste"), this, &PropertiesDock::pasteProperties); - contextMenu.addSeparator(); - QMenu *convertMenu = nullptr; - - if (customPropertiesSelected) { - convertMenu = contextMenu.addMenu(tr("Convert To")); - contextMenu.addAction(mActionRemoveProperty); - contextMenu.addAction(mActionRenameProperty); - } else { - contextMenu.addAction(mActionAddProperty); - } - - cutAction->setShortcuts(QKeySequence::Cut); - cutAction->setIcon(QIcon(QLatin1String(":/images/16/edit-cut.png"))); - cutAction->setEnabled(customPropertiesSelected && currentObjectHasAllProperties); - copyAction->setShortcuts(QKeySequence::Copy); - copyAction->setIcon(QIcon(QLatin1String(":/images/16/edit-copy.png"))); - copyAction->setEnabled(customPropertiesSelected && currentObjectHasAllProperties); - pasteAction->setShortcuts(QKeySequence::Paste); - pasteAction->setIcon(QIcon(QLatin1String(":/images/16/edit-paste.png"))); - pasteAction->setEnabled(ClipboardManager::instance()->hasProperties()); - - Utils::setThemeIcon(cutAction, "edit-cut"); - Utils::setThemeIcon(copyAction, "edit-copy"); - Utils::setThemeIcon(pasteAction, "edit-paste"); - - if (convertMenu) { - const int convertTo[] = { - QMetaType::Bool, - QMetaType::QColor, - QMetaType::Double, - filePathTypeId(), - objectRefTypeId(), - QMetaType::Int, - QMetaType::QString - }; - - // todo: could include custom property types - - for (int toType : convertTo) { - bool someDifferentType = false; - bool allCanConvert = true; - - for (const QString &propertyName : propertyNames) { - QVariant propertyValue = object->property(propertyName); - - if (propertyValue.userType() != toType) - someDifferentType = true; - - if (!propertyValue.convert(toType)) { - allCanConvert = false; - break; - } - } - - if (someDifferentType && allCanConvert) { - QAction *action = convertMenu->addAction(typeToName(toType)); - action->setData(toType); - } - } - - convertMenu->setEnabled(!convertMenu->actions().isEmpty()); - } - - ActionManager::applyMenuExtensions(&contextMenu, MenuIds::propertiesViewProperties); - - const QPoint globalPos = mPropertyBrowser->mapToGlobal(pos); - const QAction *selectedItem = contextMenu.exec(globalPos); - - if (selectedItem && convertMenu && selectedItem->parentWidget() == convertMenu) { - QUndoStack *undoStack = mDocument->undoStack(); - undoStack->beginMacro(tr("Convert Property/Properties", nullptr, items.size())); - - for (const QString &propertyName : propertyNames) { - QVariant propertyValue = object->property(propertyName); - - int toType = selectedItem->data().toInt(); - propertyValue.convert(toType); - - undoStack->push(new SetProperty(mDocument, - mDocument->currentObjects(), - propertyName, propertyValue)); - } - - undoStack->endMacro(); - } + mPropertiesWidget->selectCustomProperty(name); } bool PropertiesDock::event(QEvent *event) { switch (event->type()) { - case QEvent::ShortcutOverride: { - QKeyEvent *keyEvent = static_cast(event); - if (keyEvent->matches(QKeySequence::Delete) || keyEvent->key() == Qt::Key_Backspace - || keyEvent->matches(QKeySequence::Cut) - || keyEvent->matches(QKeySequence::Copy) - || keyEvent->matches(QKeySequence::Paste)) { - event->accept(); - return true; - } - break; - } case QEvent::LanguageChange: retranslateUi(); break; @@ -505,32 +63,16 @@ bool PropertiesDock::event(QEvent *event) return QDockWidget::event(event); } -void PropertiesDock::keyPressEvent(QKeyEvent *event) +void PropertiesDock::bringToFront() { - if (event->matches(QKeySequence::Delete) || event->key() == Qt::Key_Backspace) { - removeProperties(); - } else if (event->matches(QKeySequence::Cut)) { - cutProperties(); - } else if (event->matches(QKeySequence::Copy)) { - copyProperties(); - } else if (event->matches(QKeySequence::Paste)) { - pasteProperties(); - } else { - QDockWidget::keyPressEvent(event); - } + show(); + raise(); + mPropertiesWidget->setFocus(); } void PropertiesDock::retranslateUi() { setWindowTitle(tr("Properties")); - - mActionAddProperty->setText(tr("Add Property")); - - mActionRemoveProperty->setText(tr("Remove")); - mActionRemoveProperty->setToolTip(tr("Remove Property")); - - mActionRenameProperty->setText(tr("Rename...")); - mActionRenameProperty->setToolTip(tr("Rename Property")); } } // namespace Tiled diff --git a/src/tiled/propertiesdock.h b/src/tiled/propertiesdock.h index 624a569198..c39532484d 100644 --- a/src/tiled/propertiesdock.h +++ b/src/tiled/propertiesdock.h @@ -21,17 +21,12 @@ #pragma once #include -#include - -class QtBrowserItem; namespace Tiled { -class Object; -class Tileset; - class Document; -class PropertyBrowser; + +class PropertiesWidget; class PropertiesDock : public QDockWidget { @@ -46,34 +41,16 @@ class PropertiesDock : public QDockWidget void setDocument(Document *document); public slots: - void bringToFront(); void selectCustomProperty(const QString &name); protected: bool event(QEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; private: - void currentObjectChanged(Object *object); - void updateActions(); - - void cutProperties(); - bool copyProperties(); - void pasteProperties(); - void openAddPropertyDialog(); - void addProperty(const QString &name, const QVariant &value); - void removeProperties(); - void renameProperty(); - void renamePropertyTo(const QString &name); - void showContextMenu(const QPoint &pos); - + void bringToFront(); void retranslateUi(); - Document *mDocument; - PropertyBrowser *mPropertyBrowser; - QAction *mActionAddProperty; - QAction *mActionRemoveProperty; - QAction *mActionRenameProperty; + PropertiesWidget *mPropertiesWidget; }; } // namespace Tiled diff --git a/src/tiled/propertieswidget.cpp b/src/tiled/propertieswidget.cpp new file mode 100644 index 0000000000..e118088c2f --- /dev/null +++ b/src/tiled/propertieswidget.cpp @@ -0,0 +1,531 @@ +/* + * propertieswidget.cpp + * Copyright 2013-2023, 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 "propertieswidget.h" + +#include "actionmanager.h" +#include "addpropertydialog.h" +#include "changeproperties.h" +#include "clipboardmanager.h" +#include "mapdocument.h" +#include "mapobject.h" +#include "propertybrowser.h" +#include "utils.h" +#include "variantpropertymanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Tiled { + +PropertiesWidget::PropertiesWidget(QWidget *parent) + : QWidget{parent} + , mDocument(nullptr) + , mPropertyBrowser(new PropertyBrowser) +{ + 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(mPropertyBrowser); + layout->addWidget(toolBar); + setLayout(layout); + + mPropertyBrowser->setContextMenuPolicy(Qt::CustomContextMenu); + connect(mPropertyBrowser, &PropertyBrowser::customContextMenuRequested, + this, &PropertiesWidget::showContextMenu); + connect(mPropertyBrowser, &PropertyBrowser::selectedItemsChanged, + this, &PropertiesWidget::updateActions); + + 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); + + 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) { + if (obj->hasProperty(name)) + return true; + } + return false; +} + +void PropertiesWidget::currentObjectChanged(Object *object) +{ + mPropertyBrowser->setObject(object); + + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; + bool isTileset = object && object->isPartOfTileset(); + bool enabled = object && (!isTileset || editingTileset); + + mPropertyBrowser->setEnabled(object); + mActionAddProperty->setEnabled(enabled); +} + +void PropertiesWidget::updateActions() +{ + const QList items = mPropertyBrowser->selectedItems(); + bool allCustomProperties = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); + bool editingTileset = mDocument && mDocument->type() == Document::TilesetDocumentType; + bool isTileset = mPropertyBrowser->object() && mPropertyBrowser->object()->isPartOfTileset(); + bool canModify = allCustomProperties && (!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())) { + canModify = false; + break; + } + } + } + + mActionRemoveProperty->setEnabled(canModify); + mActionRenameProperty->setEnabled(canModify && items.size() == 1); +} + +void PropertiesWidget::cutProperties() +{ + if (copyProperties()) + removeProperties(); +} + +bool PropertiesWidget::copyProperties() +{ + Object *object = mPropertyBrowser->object(); + 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 QVariant value = object->property(name); + if (!value.isValid()) + return false; + + properties.insert(name, value); + } + + ClipboardManager::instance()->setProperties(properties); + return true; +} + +void PropertiesWidget::pasteProperties() +{ + auto clipboardManager = ClipboardManager::instance(); + + Properties pastedProperties = clipboardManager->properties(); + if (pastedProperties.isEmpty()) + return; + + const QList objects = mDocument->currentObjects(); + if (objects.isEmpty()) + return; + + QList commands; + + for (Object *object : objects) { + Properties properties = object->properties(); + mergeProperties(properties, pastedProperties); + + if (object->properties() != properties) { + commands.append(new ChangeProperties(mDocument, QString(), object, + properties)); + } + } + + if (!commands.isEmpty()) { + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock", + "Paste Property/Properties", + nullptr, + pastedProperties.size())); + + for (QUndoCommand *command : commands) + undoStack->push(command); + + undoStack->endMacro(); + } +} + +void PropertiesWidget::openAddPropertyDialog() +{ + AddPropertyDialog dialog(mPropertyBrowser); + if (dialog.exec() == AddPropertyDialog::Accepted) + addProperty(dialog.propertyName(), dialog.propertyValue()); +} + +void PropertiesWidget::addProperty(const QString &name, const QVariant &value) +{ + if (name.isEmpty()) + return; + Object *object = mDocument->currentObject(); + if (!object) + return; + + if (!object->hasProperty(name)) { + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->push(new SetProperty(mDocument, + mDocument->currentObjects(), + name, value)); + } + + mPropertyBrowser->editCustomProperty(name); +} + +void PropertiesWidget::removeProperties() +{ + Object *object = mDocument->currentObject(); + if (!object) + return; + + const QList items = mPropertyBrowser->selectedItems(); + if (items.isEmpty() || !mPropertyBrowser->allCustomPropertyItems(items)) + return; + + QStringList propertyNames; + for (QtBrowserItem *item : items) + propertyNames.append(item->property()->propertyName()); + + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->beginMacro(QCoreApplication::translate("Tiled::PropertiesDock", + "Remove Property/Properties", + nullptr, + propertyNames.size())); + + for (const QString &name : propertyNames) { + undoStack->push(new RemoveProperty(mDocument, + mDocument->currentObjects(), + name)); + } + + undoStack->endMacro(); +} + +void PropertiesWidget::renameProperty() +{ + 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->setWindowTitle(QCoreApplication::translate("Tiled::PropertiesDock", "Rename Property")); + connect(dialog, &QInputDialog::textValueSelected, this, &PropertiesWidget::renamePropertyTo); + dialog->open(); +} + +void PropertiesWidget::renamePropertyTo(const QString &name) +{ + if (name.isEmpty()) + return; + + QtBrowserItem *item = mPropertyBrowser->currentItem(); + if (!item) + return; + + const QString oldName = item->property()->propertyName(); + if (oldName == name) + return; + + QUndoStack *undoStack = mDocument->undoStack(); + undoStack->push(new RenameProperty(mDocument, mDocument->currentObjects(), oldName, name)); +} + +void PropertiesWidget::showContextMenu(const QPoint &pos) +{ + const Object *object = mDocument->currentObject(); + if (!object) + return; + + const QList items = mPropertyBrowser->selectedItems(); + const bool customPropertiesSelected = !items.isEmpty() && mPropertyBrowser->allCustomPropertyItems(items); + + bool currentObjectHasAllProperties = true; + QStringList propertyNames; + for (QtBrowserItem *item : items) { + const QString propertyName = item->property()->propertyName(); + propertyNames.append(propertyName); + + if (!object->hasProperty(propertyName)) + currentObjectHasAllProperties = false; + } + + QMenu contextMenu(mPropertyBrowser); + + 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(); + + if (!localFile.isEmpty()) { + Utils::addOpenContainingFolderAction(contextMenu, localFile); + + if (QFileInfo { localFile }.isFile()) + Utils::addOpenWithSystemEditorAction(contextMenu, localFile); + } + } 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 (!contextMenu.isEmpty()) + contextMenu.addSeparator(); + + QAction *cutAction = contextMenu.addAction(QCoreApplication::translate("Tiled::PropertiesDock", "Cu&t"), this, &PropertiesWidget::cutProperties); + QAction *copyAction = contextMenu.addAction(QCoreApplication::translate("Tiled::PropertiesDock", "&Copy"), this, &PropertiesWidget::copyProperties); + QAction *pasteAction = contextMenu.addAction(QCoreApplication::translate("Tiled::PropertiesDock", "&Paste"), this, &PropertiesWidget::pasteProperties); + contextMenu.addSeparator(); + QMenu *convertMenu = nullptr; + + if (customPropertiesSelected) { + convertMenu = contextMenu.addMenu(QCoreApplication::translate("Tiled::PropertiesDock", "Convert To")); + contextMenu.addAction(mActionRemoveProperty); + contextMenu.addAction(mActionRenameProperty); + } else { + contextMenu.addAction(mActionAddProperty); + } + + cutAction->setShortcuts(QKeySequence::Cut); + cutAction->setIcon(QIcon(QLatin1String(":/images/16/edit-cut.png"))); + cutAction->setEnabled(customPropertiesSelected && currentObjectHasAllProperties); + copyAction->setShortcuts(QKeySequence::Copy); + copyAction->setIcon(QIcon(QLatin1String(":/images/16/edit-copy.png"))); + copyAction->setEnabled(customPropertiesSelected && currentObjectHasAllProperties); + pasteAction->setShortcuts(QKeySequence::Paste); + pasteAction->setIcon(QIcon(QLatin1String(":/images/16/edit-paste.png"))); + pasteAction->setEnabled(ClipboardManager::instance()->hasProperties()); + + Utils::setThemeIcon(cutAction, "edit-cut"); + Utils::setThemeIcon(copyAction, "edit-copy"); + Utils::setThemeIcon(pasteAction, "edit-paste"); + + if (convertMenu) { + const int convertTo[] = { + QMetaType::Bool, + QMetaType::QColor, + QMetaType::Double, + filePathTypeId(), + objectRefTypeId(), + QMetaType::Int, + QMetaType::QString + }; + + // todo: could include custom property types + + for (int toType : convertTo) { + bool someDifferentType = false; + bool allCanConvert = true; + + for (const QString &propertyName : propertyNames) { + QVariant propertyValue = object->property(propertyName); + + if (propertyValue.userType() != toType) + someDifferentType = true; + + if (!propertyValue.convert(toType)) { + allCanConvert = false; + break; + } + } + + if (someDifferentType && allCanConvert) { + QAction *action = convertMenu->addAction(typeToName(toType)); + action->setData(toType); + } + } + + convertMenu->setEnabled(!convertMenu->actions().isEmpty()); + } + + ActionManager::applyMenuExtensions(&contextMenu, MenuIds::propertiesViewProperties); + + const QPoint globalPos = mPropertyBrowser->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())); + + for (const QString &propertyName : propertyNames) { + QVariant propertyValue = object->property(propertyName); + + int toType = selectedItem->data().toInt(); + propertyValue.convert(toType); + + undoStack->push(new SetProperty(mDocument, + mDocument->currentObjects(), + propertyName, propertyValue)); + } + + undoStack->endMacro(); + } +} + +bool PropertiesWidget::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ShortcutOverride: { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->matches(QKeySequence::Delete) || keyEvent->key() == Qt::Key_Backspace + || keyEvent->matches(QKeySequence::Cut) + || keyEvent->matches(QKeySequence::Copy) + || keyEvent->matches(QKeySequence::Paste)) { + event->accept(); + return true; + } + break; + } + case QEvent::LanguageChange: + retranslateUi(); + break; + default: + break; + } + + return QWidget::event(event); +} + +void PropertiesWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->matches(QKeySequence::Delete) || event->key() == Qt::Key_Backspace) { + removeProperties(); + } else if (event->matches(QKeySequence::Cut)) { + cutProperties(); + } else if (event->matches(QKeySequence::Copy)) { + copyProperties(); + } else if (event->matches(QKeySequence::Paste)) { + pasteProperties(); + } else { + QWidget::keyPressEvent(event); + } +} + +void PropertiesWidget::retranslateUi() +{ + mActionAddProperty->setText(QCoreApplication::translate("Tiled::PropertiesDock", "Add Property")); + + mActionRemoveProperty->setText(QCoreApplication::translate("Tiled::PropertiesDock", "Remove")); + mActionRemoveProperty->setToolTip(QCoreApplication::translate("Tiled::PropertiesDock", "Remove Property")); + + mActionRenameProperty->setText(QCoreApplication::translate("Tiled::PropertiesDock", "Rename...")); + mActionRenameProperty->setToolTip(QCoreApplication::translate("Tiled::PropertiesDock", "Rename Property")); +} + +} // namespace Tiled + +#include "moc_propertieswidget.cpp" diff --git a/src/tiled/propertieswidget.h b/src/tiled/propertieswidget.h new file mode 100644 index 0000000000..022a520b2f --- /dev/null +++ b/src/tiled/propertieswidget.h @@ -0,0 +1,83 @@ +/* + * propertieswidget.h + * Copyright 2013-2023, 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 + +namespace Tiled { + +class Object; + +class Document; +class PropertyBrowser; + +/** + * The PropertiesWidget combines the PropertyBrowser with some controls that + * allow adding and removing properties. It also implements cut, copy and paste + * actions and the context menu. + */ +class PropertiesWidget : public QWidget +{ + Q_OBJECT + +public: + explicit PropertiesWidget(QWidget *parent = nullptr); + ~PropertiesWidget() override; + + /** + * Sets the \a document on which this properties dock will act. + */ + void setDocument(Document *document); + +signals: + void bringToFront(); + +public slots: + void selectCustomProperty(const QString &name); + +protected: + bool event(QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + +private: + void currentObjectChanged(Object *object); + void updateActions(); + + void cutProperties(); + bool copyProperties(); + void pasteProperties(); + void openAddPropertyDialog(); + void addProperty(const QString &name, const QVariant &value); + void removeProperties(); + void renameProperty(); + void renamePropertyTo(const QString &name); + void showContextMenu(const QPoint &pos); + + void retranslateUi(); + + Document *mDocument; + PropertyBrowser *mPropertyBrowser; + QAction *mActionAddProperty; + QAction *mActionRemoveProperty; + QAction *mActionRenameProperty; +}; + +} // namespace Tiled diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp index f1f5636523..1b1850b873 100644 --- a/src/tiled/propertybrowser.cpp +++ b/src/tiled/propertybrowser.cpp @@ -488,6 +488,7 @@ static void addAutomappingProperties(Properties &properties, const Object *objec case Object::TileType: case Object::WangSetType: case Object::WangColorType: + case Object::ProjectType: break; } } @@ -695,6 +696,7 @@ void PropertyBrowser::valueChanged(QtProperty *property, const QVariant &val) 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; } } @@ -1802,6 +1804,7 @@ void PropertyBrowser::addProperties() case Object::TileType: addTileProperties(); break; case Object::WangSetType: addWangSetProperties(); break; case Object::WangColorType: addWangColorProperties(); break; + case Object::ProjectType: break; } // Make sure certain properties are collapsed, to save space @@ -1841,7 +1844,8 @@ void PropertyBrowser::updateProperties() QScopedValueRollback updating(mUpdating, true); - mIdToProperty[ClassProperty]->setValue(mObject->className()); + if (auto classProperty = mIdToProperty.value(ClassProperty)) + classProperty->setValue(mObject->className()); switch (mObject->typeId()) { case Object::MapType: { @@ -2017,6 +2021,8 @@ void PropertyBrowser::updateProperties() mIdToProperty[WangColorProbabilityProperty]->setValue(wangColor->probability()); break; } + case Object::ProjectType: + break; } } diff --git a/src/tiled/propertytypeseditor.cpp b/src/tiled/propertytypeseditor.cpp index d0cae9c5e8..c6b4d19c15 100644 --- a/src/tiled/propertytypeseditor.cpp +++ b/src/tiled/propertytypeseditor.cpp @@ -250,7 +250,7 @@ PropertyTypesEditor::PropertyTypesEditor(QWidget *parent) Preferences *prefs = Preferences::instance(); - auto &project = ProjectManager::instance()->project(); + const auto &project = ProjectManager::instance()->project(); mPropertyTypesModel->setPropertyTypes(project.propertyTypes()); connect(prefs, &Preferences::propertyTypesChanged, @@ -412,7 +412,7 @@ void PropertyTypesEditor::propertyTypesChanged() if (mSettingPrefPropertyTypes) return; - auto &project = ProjectManager::instance()->project(); + const auto &project = ProjectManager::instance()->project(); mPropertyTypesModel->setPropertyTypes(project.propertyTypes()); selectedPropertyTypesChanged(); diff --git a/src/tiled/scriptmodule.cpp b/src/tiled/scriptmodule.cpp index 85c64b7e1e..a5f558eeff 100644 --- a/src/tiled/scriptmodule.cpp +++ b/src/tiled/scriptmodule.cpp @@ -214,11 +214,9 @@ QList ScriptModule::openAssets() const return assets; } -EditableProject *ScriptModule::project() +EditableAsset *ScriptModule::project() { - if (!mEditableProject) - mEditableProject = new EditableProject(&ProjectManager::instance()->project(), this); - return mEditableProject; + return ProjectManager::instance()->editableProject(); } TilesetEditor *ScriptModule::tilesetEditor() const diff --git a/src/tiled/scriptmodule.h b/src/tiled/scriptmodule.h index f7fdbbf3a8..c7f348cf6f 100644 --- a/src/tiled/scriptmodule.h +++ b/src/tiled/scriptmodule.h @@ -23,7 +23,6 @@ #include "id.h" #include "issuesdock.h" #include "properties.h" -#include "editableproject.h" #include #include @@ -70,7 +69,7 @@ class ScriptModule : public QObject Q_PROPERTY(Tiled::EditableAsset *activeAsset READ activeAsset WRITE setActiveAsset NOTIFY activeAssetChanged) Q_PROPERTY(QList openAssets READ openAssets) - Q_PROPERTY(Tiled::EditableProject *project READ project) + Q_PROPERTY(Tiled::EditableAsset *project READ project) Q_PROPERTY(Tiled::MapEditor *mapEditor READ mapEditor) Q_PROPERTY(Tiled::TilesetEditor *tilesetEditor READ tilesetEditor) @@ -100,7 +99,7 @@ class ScriptModule : public QObject QList openAssets() const; - EditableProject *project(); + EditableAsset *project(); TilesetEditor *tilesetEditor() const; MapEditor *mapEditor() const; @@ -176,7 +175,6 @@ public slots: std::map> mRegisteredTools; QStringList mScriptArguments; - EditableProject *mEditableProject = nullptr; }; inline bool ScriptModule::versionLessThan(const QString &a)