diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 49014570a2..c2c51da586 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,7 +44,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qtdeclarative5-private-dev qtbase5-private-dev qbs python3-dev + sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qbs python3-dev - name: Setup qbs run: | diff --git a/.github/workflows/ubuntu-22.04.yml b/.github/workflows/ubuntu-22.04.yml index 2012bf8f50..f31331da09 100644 --- a/.github/workflows/ubuntu-22.04.yml +++ b/.github/workflows/ubuntu-22.04.yml @@ -32,7 +32,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qtdeclarative5-private-dev qtbase5-private-dev qbs python3-dev + sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qbs python3-dev - name: Setup qbs run: | diff --git a/README.md b/README.md index 2e184beceb..c00ff266ff 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Compiling Tiled Before you can compile Tiled, you must ensure the Qt (>= 5.12) development libraries have been installed as well as the Qbs build tool: -* On Ubuntu/Debian: `sudo apt install qtbase5-dev qtbase5-private-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qtdeclarative5-private-dev qbs` +* On Ubuntu/Debian: `sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev-tools zlib1g-dev qtdeclarative5-dev qbs` * On Fedora: `sudo dnf builddep tiled` * On Arch Linux: `sudo pacman -S qt qt5-tools qbs` * On macOS with [Homebrew](https://brew.sh/): diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fc73fd44a3..027dbc80b3 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -59,8 +59,6 @@ parts: - qbs - libqt5svg5-dev - qtdeclarative5-dev - - qtdeclarative5-private-dev - - qtbase5-private-dev - zlib1g-dev - libzstd-dev stage-packages: diff --git a/src/libtiled/object.cpp b/src/libtiled/object.cpp index 409fa607ca..3f194f157b 100644 --- a/src/libtiled/object.cpp +++ b/src/libtiled/object.cpp @@ -34,8 +34,11 @@ namespace Tiled { SharedPropertyTypes Object::mPropertyTypes; + Object::~Object() -{} +{ + delete mEditable; +} /** * Returns the value of the property \a name, taking into account that it may @@ -87,19 +90,19 @@ QVariantMap Object::resolvedProperties() const if (auto type = propertyTypes().findClassFor(objectClassName, *this)) Tiled::mergeProperties(allProperties, type->members); - + if (typeId() == Object::MapObjectType) { auto mapObject = static_cast(this); if (const Tile *tile = mapObject->cell().tile()) Tiled::mergeProperties(allProperties, tile->properties()); - + if (const MapObject *templateObject = mapObject->templateObject()) Tiled::mergeProperties(allProperties, templateObject->properties()); } Tiled::mergeProperties(allProperties, properties()); - + return allProperties; } diff --git a/src/libtiled/object.h b/src/libtiled/object.h index b346e16466..3c05ec1196 100644 --- a/src/libtiled/object.h +++ b/src/libtiled/object.h @@ -31,6 +31,8 @@ #include "properties.h" #include "propertytype.h" +#include + namespace Tiled { /** @@ -152,6 +154,12 @@ class TILEDSHARED_EXPORT Object QString mClassName; Properties mProperties; + /** + * The editable wrapper created for this object. + */ + QPointer mEditable; + friend class EditableObject; + static SharedPropertyTypes mPropertyTypes; }; diff --git a/src/libtiled/tilesetmanager.cpp b/src/libtiled/tilesetmanager.cpp index 11f3a578f9..3a78844d3c 100644 --- a/src/libtiled/tilesetmanager.cpp +++ b/src/libtiled/tilesetmanager.cpp @@ -35,6 +35,8 @@ #include "tileanimationdriver.h" #include "tilesetformat.h" +#include + namespace Tiled { TilesetManager *TilesetManager::mInstance; @@ -57,7 +59,10 @@ TilesetManager::TilesetManager(): TilesetManager::~TilesetManager() { // Assert that there are no remaining tileset instances - Q_ASSERT(mTilesets.isEmpty()); + if (!mTilesets.isEmpty()) { + qWarning() << "TilesetManager: There are still" << mTilesets.size() + << "tilesets loaded at exit!"; + } } /** diff --git a/src/tiled/addremovelayer.cpp b/src/tiled/addremovelayer.cpp index aaa911a685..d9ee799a0c 100644 --- a/src/tiled/addremovelayer.cpp +++ b/src/tiled/addremovelayer.cpp @@ -43,18 +43,17 @@ AddRemoveLayer::AddRemoveLayer(MapDocument *mapDocument, AddRemoveLayer::~AddRemoveLayer() { - delete mLayer; } void AddRemoveLayer::addLayer() { - mMapDocument->layerModel()->insertLayer(mParentLayer, mIndex, mLayer); - mLayer = nullptr; + mMapDocument->layerModel()->insertLayer(mParentLayer, mIndex, + mLayer.release()); } void AddRemoveLayer::removeLayer() { - mLayer = mMapDocument->layerModel()->takeLayerAt(mParentLayer, mIndex); + mLayer.reset(mMapDocument->layerModel()->takeLayerAt(mParentLayer, mIndex)); } AddLayer::AddLayer(MapDocument *mapDocument, diff --git a/src/tiled/addremovelayer.h b/src/tiled/addremovelayer.h index 68b7b2729a..3a86e84032 100644 --- a/src/tiled/addremovelayer.h +++ b/src/tiled/addremovelayer.h @@ -24,6 +24,8 @@ #include +#include + namespace Tiled { class GroupLayer; @@ -48,7 +50,7 @@ class AddRemoveLayer : public QUndoCommand void removeLayer(); MapDocument *mMapDocument; - Layer *mLayer; + std::unique_ptr mLayer; GroupLayer *mParentLayer; int mIndex; }; diff --git a/src/tiled/editablegrouplayer.cpp b/src/tiled/editablegrouplayer.cpp index a8e8875017..78164d71cc 100644 --- a/src/tiled/editablegrouplayer.cpp +++ b/src/tiled/editablegrouplayer.cpp @@ -22,7 +22,6 @@ #include "addremovelayer.h" #include "addremovetileset.h" -#include "editablemanager.h" #include "editablemap.h" #include "scriptmanager.h" @@ -43,11 +42,10 @@ EditableGroupLayer::EditableGroupLayer(EditableMap *map, GroupLayer *groupLayer, QList EditableGroupLayer::layers() { QList editables; - auto &editableManager = EditableManager::instance(); auto editableMap = map(); for (const auto layer : groupLayer()->layers()) - editables.append(editableManager.editableLayer(editableMap, layer)); + editables.append(EditableLayer::get(editableMap, layer)); return editables; } @@ -60,7 +58,7 @@ EditableLayer *EditableGroupLayer::layerAt(int index) } Layer *layer = groupLayer()->layerAt(index); - return EditableManager::instance().editableLayer(map(), layer); + return EditableLayer::get(map(), layer); } void EditableGroupLayer::removeLayerAt(int index) @@ -73,7 +71,7 @@ void EditableGroupLayer::removeLayerAt(int index) if (MapDocument *doc = mapDocument()) asset()->push(new RemoveLayer(doc, index, groupLayer())); else if (!checkReadOnly()) - EditableManager::instance().release(groupLayer()->takeLayerAt(index)); + EditableLayer::release(groupLayer()->takeLayerAt(index)); } void EditableGroupLayer::removeLayer(EditableLayer *editableLayer) @@ -100,7 +98,7 @@ void EditableGroupLayer::insertLayerAt(int index, EditableLayer *editableLayer) } if (!editableLayer) { - ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Invalid argument")); + ScriptManager::instance().throwNullArgError(1); return; } @@ -124,12 +122,17 @@ void EditableGroupLayer::insertLayerAt(int index, EditableLayer *editableLayer) map->addTilesets(tilesets); // ownership moves to the group layer - groupLayer()->insertLayer(index, editableLayer->release()); + groupLayer()->insertLayer(index, editableLayer->attach(asset())); } } void EditableGroupLayer::addLayer(EditableLayer *editableLayer) { + if (!editableLayer) { + ScriptManager::instance().throwNullArgError(0); + return; + } + insertLayerAt(layerCount(), editableLayer); } diff --git a/src/tiled/editablelayer.cpp b/src/tiled/editablelayer.cpp index 5efaa266cf..24115ebcf7 100644 --- a/src/tiled/editablelayer.cpp +++ b/src/tiled/editablelayer.cpp @@ -22,8 +22,10 @@ #include "changelayer.h" #include "editablegrouplayer.h" -#include "editablemanager.h" +#include "editableimagelayer.h" #include "editablemap.h" +#include "editableobjectgroup.h" +#include "editabletilelayer.h" namespace Tiled { @@ -31,7 +33,6 @@ EditableLayer::EditableLayer(std::unique_ptr layer, QObject *parent) : EditableObject(nullptr, layer.get(), parent) { mDetachedLayer = std::move(layer); - EditableManager::instance().mEditables.insert(this->layer(), this); } EditableLayer::EditableLayer(EditableAsset *asset, Layer *layer, QObject *parent) @@ -41,7 +42,9 @@ EditableLayer::EditableLayer(EditableAsset *asset, Layer *layer, QObject *parent EditableLayer::~EditableLayer() { - EditableManager::instance().remove(this); + // Prevent owned object from trying to delete us again + if (mDetachedLayer) + setObject(nullptr); } EditableMap *EditableLayer::map() const @@ -53,7 +56,7 @@ EditableMap *EditableLayer::map() const EditableGroupLayer *EditableLayer::parentLayer() const { GroupLayer *parent = layer()->parentLayer(); - return static_cast(EditableManager::instance().editableLayer(map(), parent)); + return static_cast(EditableLayer::get(map(), parent)); } bool EditableLayer::isSelected() const @@ -70,47 +73,85 @@ bool EditableLayer::isSelected() const void EditableLayer::detach() { Q_ASSERT(asset()); - - EditableManager::instance().remove(this); setAsset(nullptr); + if (!moveOwnershipToJavaScript()) + return; + mDetachedLayer.reset(layer()->clone()); // mDetachedLayer->resetIds(); setObject(mDetachedLayer.get()); - EditableManager::instance().mEditables.insert(layer(), this); } /** * Turns this stand-alone layer into a reference, with the layer now owned by - * the given asset. + * a map, group layer or tile (in case of object group). + * + * The given \a asset may be a nullptr in case the layer is added to a group + * layer which isn't part of a map. + * + * Returns nullptr if the editable wasn't owning its layer. */ -void EditableLayer::attach(EditableAsset *asset) +Layer *EditableLayer::attach(EditableAsset *asset) { - Q_ASSERT(!this->asset() && asset); + Q_ASSERT(!this->asset()); setAsset(asset); - mDetachedLayer.release(); + moveOwnershipToCpp(); + return mDetachedLayer.release(); } /** - * Take ownership of the referenced layer. + * Take ownership of the referenced layer or delete it. */ -void EditableLayer::hold() +void EditableLayer::hold(std::unique_ptr layer) { - Q_ASSERT(!asset()); // if asset exists, it holds the layer (possibly indirectly) Q_ASSERT(!mDetachedLayer); // can't already be holding the layer + Q_ASSERT(this->layer() == layer.get()); - mDetachedLayer.reset(layer()); + if (!moveOwnershipToJavaScript()) + return; + + setAsset(nullptr); + mDetachedLayer = std::move(layer); } -/** - * Release ownership of the referenced layer. - */ -Layer *EditableLayer::release() +EditableLayer *EditableLayer::get(EditableMap *map, Layer *layer) { - Q_ASSERT(isOwning()); + if (!layer) + return nullptr; - return mDetachedLayer.release(); + auto editable = find(layer); + if (editable) + return editable; + + Q_ASSERT(!map || layer->map() == map->map()); + + switch (layer->layerType()) { + case Layer::TileLayerType: + editable = new EditableTileLayer(map, static_cast(layer)); + break; + case Layer::ObjectGroupType: + editable = new EditableObjectGroup(map, static_cast(layer)); + break; + case Layer::ImageLayerType: + editable = new EditableImageLayer(map, static_cast(layer)); + break; + case Layer::GroupLayerType: + editable = new EditableGroupLayer(map, static_cast(layer)); + break; + } + + editable->moveOwnershipToCpp(); + + return editable; +} + +void EditableLayer::release(Layer *layer) +{ + std::unique_ptr owned { layer }; + if (auto editable = EditableLayer::find(layer)) + editable->hold(std::move(owned)); } void EditableLayer::setName(const QString &name) diff --git a/src/tiled/editablelayer.h b/src/tiled/editablelayer.h index c53ba4bfa2..b39389e1aa 100644 --- a/src/tiled/editablelayer.h +++ b/src/tiled/editablelayer.h @@ -88,11 +88,14 @@ class EditableLayer : public EditableObject Layer *layer() const; void detach(); - void attach(EditableAsset *asset); - void hold(); - Layer *release(); + Layer *attach(EditableAsset *asset); + void hold(std::unique_ptr layer); bool isOwning() const; + static EditableLayer *find(Layer *layer); + static EditableLayer *get(EditableMap *map, Layer *layer); + static void release(Layer *layer); + public slots: void setName(const QString &name); void setOpacity(qreal opacity); @@ -182,6 +185,11 @@ inline bool EditableLayer::isOwning() const return mDetachedLayer.get() == layer(); } +inline EditableLayer *EditableLayer::find(Layer *layer) +{ + return static_cast(EditableObject::find(layer)); +} + } // namespace Tiled Q_DECLARE_METATYPE(Tiled::EditableLayer*) diff --git a/src/tiled/editablemanager.cpp b/src/tiled/editablemanager.cpp deleted file mode 100644 index 858cbd54a8..0000000000 --- a/src/tiled/editablemanager.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * editablemanager.cpp - * Copyright 2019, 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 "editablemanager.h" - -#include "documentmanager.h" -#include "editablegrouplayer.h" -#include "editableimagelayer.h" -#include "editablemap.h" -#include "editableobjectgroup.h" -#include "editabletile.h" -#include "editabletilelayer.h" -#include "editabletileset.h" -#include "editablewangset.h" -#include "scriptmanager.h" -#include "wangset.h" - -#include - -#include - -namespace Tiled { - -template -static T *checkNull(T *object) -{ - return QQmlData::wasDeleted(object) ? nullptr : object; -} - -std::unique_ptr EditableManager::mInstance; - -EditableManager::EditableManager(QObject *parent) - : QObject(parent) -{ -} - -EditableManager &EditableManager::instance() -{ - if (!mInstance) - mInstance.reset(new EditableManager); - return *mInstance; -} - -void EditableManager::deleteInstance() -{ - mInstance.reset(); -} - -EditableTileset *Tiled::EditableManager::find(Tileset *tileset) const -{ - return static_cast(checkNull(mEditables.value(tileset))); -} - -EditableLayer *EditableManager::find(Layer *layer) const -{ - return static_cast(checkNull(mEditables.value(layer))); -} - -EditableMapObject *EditableManager::find(MapObject *mapObject) const -{ - return static_cast(checkNull(mEditables.value(mapObject))); -} - -EditableTile *EditableManager::find(Tile *tile) const -{ - return static_cast(checkNull(mEditables.value(tile))); -} - -EditableWangSet *EditableManager::find(WangSet *wangSet) const -{ - return static_cast(checkNull(mEditables.value(wangSet))); -} - -void EditableManager::remove(EditableObject *editable) -{ - auto it = mEditables.find(editable->object()); - if (it != mEditables.end() && *it == editable) - mEditables.erase(it); -} - -void EditableManager::release(Layer *layer) -{ - if (EditableLayer *editable = find(layer)) - editable->hold(); - else - delete layer; -} - -void EditableManager::release(MapObject *mapObject) -{ - if (EditableMapObject *editable = find(mapObject)) - editable->hold(); - else - delete mapObject; -} - -/** - * Releases the WangSet by either finding an EditableWangSet instance to take - * ownership of it or deleting it. - */ -void EditableManager::release(std::unique_ptr wangSet) -{ - if (EditableWangSet *editable = find(wangSet.get())) { - editable->hold(); - wangSet.release(); - } -} - -EditableLayer *EditableManager::editableLayer(EditableMap *map, Layer *layer) -{ - if (!layer) - return nullptr; - - Q_ASSERT(!map || layer->map() == map->map()); - - EditableObject* &editable = mEditables[layer]; - if (QQmlData::wasDeleted(editable)) { - switch (layer->layerType()) { - case Layer::TileLayerType: - editable = new EditableTileLayer(map, static_cast(layer)); - break; - case Layer::ObjectGroupType: - editable = new EditableObjectGroup(map, static_cast(layer)); - break; - case Layer::ImageLayerType: - editable = new EditableImageLayer(map, static_cast(layer)); - break; - case Layer::GroupLayerType: - editable = new EditableGroupLayer(map, static_cast(layer)); - break; - } - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -EditableObjectGroup *EditableManager::editableObjectGroup(EditableAsset *asset, ObjectGroup *objectGroup) -{ - if (!objectGroup) - return nullptr; - - EditableObject* &editable = mEditables[objectGroup]; - if (QQmlData::wasDeleted(editable)) { - editable = new EditableObjectGroup(asset, objectGroup); - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -EditableMapObject *EditableManager::editableMapObject(EditableAsset *asset, MapObject *mapObject) -{ - if (!mapObject) - return nullptr; - - Q_ASSERT(mapObject->objectGroup()); - - EditableObject* &editable = mEditables[mapObject]; - if (QQmlData::wasDeleted(editable)) { - editable = new EditableMapObject(asset, mapObject); - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -EditableTileset *EditableManager::editableTileset(Tileset *tileset) -{ - if (!tileset) - return nullptr; - - if (auto document = TilesetDocument::findDocumentForTileset(tileset->sharedFromThis())) - return document->editable(); - - EditableObject* &editable = mEditables[tileset]; - if (QQmlData::wasDeleted(editable)) { - editable = new EditableTileset(tileset); - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -EditableTile *EditableManager::editableTile(Tile *tile) -{ - if (!tile) - return nullptr; - - EditableTileset *tileset = editableTileset(tile->tileset()); - return editableTile(tileset, tile); -} - -EditableTile *EditableManager::editableTile(EditableTileset *tileset, Tile *tile) -{ - Q_ASSERT(tile); - Q_ASSERT(tile->tileset() == tileset->tileset()); - - EditableObject* &editable = mEditables[tile]; - if (QQmlData::wasDeleted(editable)) { - editable = new EditableTile(tileset, tile); - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -EditableWangSet *EditableManager::editableWangSet(WangSet *wangSet) -{ - if (!wangSet) - return nullptr; - - EditableTileset *tileset = editableTileset(wangSet->tileset()); - return editableWangSet(tileset, wangSet); -} - -EditableWangSet *EditableManager::editableWangSet(EditableTileset *tileset, WangSet *wangSet) -{ - Q_ASSERT(wangSet); - Q_ASSERT(wangSet->tileset() == tileset->tileset()); - - EditableObject* &editable = mEditables[wangSet]; - if (QQmlData::wasDeleted(editable)) { - editable = new EditableWangSet(tileset, wangSet); - QQmlEngine::setObjectOwnership(editable, QQmlEngine::JavaScriptOwnership); - } - - return static_cast(editable); -} - -} // namespace Tiled - -#include "moc_editablemanager.cpp" diff --git a/src/tiled/editablemanager.h b/src/tiled/editablemanager.h deleted file mode 100644 index 876ac944b4..0000000000 --- a/src/tiled/editablemanager.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * editablemanager.h - * Copyright 2019, 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 - -namespace Tiled { - -class Layer; -class MapObject; -class Object; -class ObjectGroup; -class Tile; -class Tileset; -class WangSet; - -class EditableAsset; -class EditableLayer; -class EditableMap; -class EditableMapObject; -class EditableObject; -class EditableObjectGroup; -class EditableTile; -class EditableTileset; -class EditableWangSet; - -/** - * Manages editable wrappers that are used to expose properties to scripts. - */ -class EditableManager : public QObject -{ - Q_OBJECT - - explicit EditableManager(QObject *parent = nullptr); - -public: - static EditableManager &instance(); - static void deleteInstance(); - - EditableLayer *find(Layer *layer) const; - EditableMapObject *find(MapObject *mapObject) const; - EditableTile *find(Tile *tile) const; - EditableTileset *find(Tileset *tileset) const; - EditableWangSet *find(WangSet *wangSet) const; - - void remove(EditableObject *editable); - - void release(Layer *layer); - void release(MapObject *mapObject); - void release(std::unique_ptr wangSet); - - EditableLayer *editableLayer(EditableMap *map, Layer *layer); - EditableObjectGroup *editableObjectGroup(EditableAsset *asset, ObjectGroup *objectGroup); - EditableMapObject *editableMapObject(EditableAsset *asset, MapObject *mapObject); - EditableTileset *editableTileset(Tileset *tileset); - EditableTile *editableTile(Tile *tile); - EditableTile *editableTile(EditableTileset *tileset, Tile *tile); - EditableWangSet *editableWangSet(WangSet *wangSet); - EditableWangSet *editableWangSet(EditableTileset *tileset, WangSet *wangSet); - -private: - friend class EditableLayer; - friend class EditableMapObject; - friend class EditableTileset; - friend class EditableTile; - friend class EditableWangSet; - - QHash mEditables; - - static std::unique_ptr mInstance; -}; - -} // namespace Tiled diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index 95acf8bd9d..c126681a2a 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -27,7 +27,6 @@ #include "changeevents.h" #include "changemapproperty.h" #include "editablelayer.h" -#include "editablemanager.h" #include "editablemapobject.h" #include "editableselectedarea.h" #include "editabletilelayer.h" @@ -93,15 +92,18 @@ EditableMap::~EditableMap() { for (Layer *layer : map()->layers()) detachLayer(layer); + + // Prevent owned object from trying to delete us again + if (mDetachedMap) + setObject(nullptr); } QList EditableMap::tilesets() const { QList editableTilesets; - auto &editableManager = EditableManager::instance(); for (const SharedTileset &tileset : map()->tilesets()) - editableTilesets.append(editableManager.editableTileset(tileset.data())); + editableTilesets.append(EditableTileset::get(tileset.data())); return editableTilesets; } @@ -109,10 +111,9 @@ QList EditableMap::tilesets() const QList EditableMap::layers() { QList editables; - auto &editableManager = EditableManager::instance(); for (const auto layer : map()->layers()) - editables.append(editableManager.editableLayer(this, layer)); + editables.append(EditableLayer::get(this, layer)); return editables; } @@ -120,7 +121,7 @@ QList EditableMap::layers() EditableLayer *EditableMap::currentLayer() { if (auto document = mapDocument()) - return EditableManager::instance().editableLayer(this, document->currentLayer()); + return EditableLayer::get(this, document->currentLayer()); return nullptr; } @@ -131,10 +132,9 @@ QList EditableMap::selectedLayers() QList selectedLayers; - auto &editableManager = EditableManager::instance(); const auto selectedLayersOrdered = mapDocument()->selectedLayersOrdered(); for (Layer *layer : selectedLayersOrdered) - selectedLayers.append(editableManager.editableLayer(this, layer)); + selectedLayers.append(EditableLayer::get(this, layer)); return selectedLayers; } @@ -146,10 +146,9 @@ QList EditableMap::selectedObjects() QList selectedObjects; - auto &editableManager = EditableManager::instance(); const auto selectedObjectsOrdered = mapDocument()->selectedObjectsOrdered(); for (MapObject *object : selectedObjectsOrdered) - selectedObjects.append(editableManager.editableMapObject(this, object)); + selectedObjects.append(EditableMapObject::get(this, object)); return selectedObjects; } @@ -162,7 +161,7 @@ EditableLayer *EditableMap::layerAt(int index) } Layer *layer = map()->layerAt(index); - return EditableManager::instance().editableLayer(this, layer); + return EditableLayer::get(this, layer); } void EditableMap::removeLayerAt(int index) @@ -176,7 +175,7 @@ void EditableMap::removeLayerAt(int index) push(new RemoveLayer(doc, index, nullptr)); } else if (!checkReadOnly()) { auto layer = map()->takeLayerAt(index); - EditableManager::instance().release(layer); + EditableLayer::release(layer); } } @@ -216,12 +215,12 @@ void EditableMap::insertLayerAt(int index, EditableLayer *editableLayer) } if (!editableLayer) { - ScriptManager::instance().throwNullArgError(0); + ScriptManager::instance().throwNullArgError(1); return; } - if (editableLayer->map()) { - ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Layer already part of a map")); + if (!editableLayer->isOwning()) { + ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Layer is in use")); return; } @@ -244,12 +243,17 @@ void EditableMap::insertLayerAt(int index, EditableLayer *editableLayer) map()->addTilesets(tilesets); // ownership moves to the map - map()->insertLayer(index, editableLayer->release()); + map()->insertLayer(index, editableLayer->attach(this)); } } void EditableMap::addLayer(EditableLayer *editableLayer) { + if (!editableLayer) { + ScriptManager::instance().throwNullArgError(0); + return; + } + insertLayerAt(layerCount(), editableLayer); } @@ -333,7 +337,7 @@ QList EditableMap::usedTilesets() const QList editableTilesets; for (const SharedTileset &tileset : tilesets) - editableTilesets.append(EditableManager::instance().editableTileset(tileset.data())); + editableTilesets.append(EditableTileset::get(tileset.data())); return editableTilesets; } @@ -361,9 +365,9 @@ void EditableMap::removeObjects(const QList &objects) if (auto doc = mapDocument()) { asset()->push(new RemoveMapObjects(doc, mapObjects)); } else { - for (MapObject *mapObject : mapObjects) { + for (MapObject *mapObject : std::as_const(mapObjects)) { mapObject->objectGroup()->removeObject(mapObject); - EditableManager::instance().release(mapObject); + EditableMapObject::release(mapObject); } } } @@ -671,6 +675,8 @@ QSharedPointer EditableMap::createDocument() auto document = MapDocumentPtr::create(std::move(mDetachedMap)); document->setEditable(std::unique_ptr(this)); + mSelectedArea = new EditableSelectedArea(document.data(), this); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); return document; @@ -696,7 +702,7 @@ void EditableMap::documentChanged(const ChangeEvent &change) void EditableMap::attachLayer(Layer *layer) { - if (EditableLayer *editable = EditableManager::instance().find(layer)) + if (auto editable = EditableLayer::find(layer)) editable->attach(this); if (GroupLayer *groupLayer = layer->asGroupLayer()) { @@ -709,7 +715,7 @@ void EditableMap::attachLayer(Layer *layer) void EditableMap::detachLayer(Layer *layer) { - auto editableLayer = EditableManager::instance().find(layer); + auto editableLayer = EditableLayer::find(layer); if (editableLayer && editableLayer->map() == this) editableLayer->detach(); @@ -723,18 +729,16 @@ void EditableMap::detachLayer(Layer *layer) void EditableMap::attachMapObjects(const QList &mapObjects) { - const auto &editableManager = EditableManager::instance(); for (MapObject *mapObject : mapObjects) { - if (EditableMapObject *editable = editableManager.find(mapObject)) + if (auto editable = EditableMapObject::find(mapObject)) editable->attach(this); } } void EditableMap::detachMapObjects(const QList &mapObjects) { - const auto &editableManager = EditableManager::instance(); for (MapObject *mapObject : mapObjects) { - if (EditableMapObject *editable = editableManager.find(mapObject)) { + if (auto editable = EditableMapObject::find(mapObject)) { Q_ASSERT(editable->map() == this); editable->detach(); } @@ -743,8 +747,7 @@ void EditableMap::detachMapObjects(const QList &mapObjects) void EditableMap::onRegionEdited(const QRegion ®ion, TileLayer *layer) { - auto &editableManager = EditableManager::instance(); - const auto editableLayer = static_cast(editableManager.editableLayer(this, layer)); + const auto editableLayer = static_cast(EditableLayer::get(this, layer)); emit regionEdited(RegionValueType(region), editableLayer); } diff --git a/src/tiled/editablemapobject.cpp b/src/tiled/editablemapobject.cpp index 1f2bd6efae..7c6b8b9e3d 100644 --- a/src/tiled/editablemapobject.cpp +++ b/src/tiled/editablemapobject.cpp @@ -22,11 +22,12 @@ #include "changemapobject.h" #include "changepolygon.h" -#include "editablemanager.h" #include "editablemap.h" #include "editableobjectgroup.h" #include "editabletile.h" +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include "scriptmanager.h" +#endif #include #include @@ -46,7 +47,6 @@ EditableMapObject::EditableMapObject(Shape shape, mapObject()->setShape(static_cast(shape)); mDetachedMapObject.reset(mapObject()); - EditableManager::instance().mEditables.insert(mapObject(), this); } EditableMapObject::EditableMapObject(EditableAsset *asset, @@ -58,13 +58,17 @@ EditableMapObject::EditableMapObject(EditableAsset *asset, EditableMapObject::~EditableMapObject() { - EditableManager::instance().remove(this); + // Prevent owned object from trying to delete us again + if (mDetachedMapObject) + setObject(nullptr); } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QJSValue EditableMapObject::polygon() const { - QJSEngine *engine = ScriptManager::instance().engine(); + QJSEngine *engine = qjsEngine(this); + if (!engine) + return QJSValue(); const auto &polygon = mapObject()->polygon(); QJSValue array = engine->newArray(polygon.size()); @@ -82,7 +86,7 @@ QJSValue EditableMapObject::polygon() const EditableTile *EditableMapObject::tile() const { - return EditableManager::instance().editableTile(mapObject()->cell().tile()); + return EditableTile::get(mapObject()->cell().tile()); } bool EditableMapObject::isSelected() const @@ -95,7 +99,7 @@ bool EditableMapObject::isSelected() const EditableObjectGroup *EditableMapObject::layer() const { - return EditableManager::instance().editableObjectGroup(asset(), mapObject()->objectGroup()); + return EditableObjectGroup::get(asset(), mapObject()->objectGroup()); } EditableMap *EditableMapObject::map() const @@ -106,36 +110,63 @@ EditableMap *EditableMapObject::map() const void EditableMapObject::detach() { Q_ASSERT(asset()); - - EditableManager::instance().remove(this); setAsset(nullptr); + if (!moveOwnershipToJavaScript()) + return; + mDetachedMapObject.reset(mapObject()->clone()); setObject(mDetachedMapObject.get()); - EditableManager::instance().mEditables.insert(mapObject(), this); } -void EditableMapObject::attach(EditableMap *map) +/** + * Turns this stand-alone object into a reference, with the object now owned by + * an object group. The given \a asset may be nullptr. + * + * Returns nullptr if the editable wasn't owning its object. + */ +MapObject *EditableMapObject::attach(EditableAsset *asset) { - Q_ASSERT(!asset() && map); + Q_ASSERT(!this->asset()); - setAsset(map); - mDetachedMapObject.release(); + setAsset(asset); + moveOwnershipToCpp(); + return mDetachedMapObject.release(); } -void EditableMapObject::hold() +void EditableMapObject::hold(std::unique_ptr mapObject) { - Q_ASSERT(!asset()); // if asset exists, it holds the object (possibly indirectly) Q_ASSERT(!mDetachedMapObject); // can't already be holding the object + Q_ASSERT(this->mapObject() == mapObject.get()); - mDetachedMapObject.reset(mapObject()); + if (!moveOwnershipToJavaScript()) + return; + + setAsset(nullptr); + mDetachedMapObject = std::move(mapObject); } -void EditableMapObject::release() +EditableMapObject *EditableMapObject::get(EditableAsset *asset, MapObject *mapObject) { - Q_ASSERT(mDetachedMapObject.get() == mapObject()); + if (!mapObject) + return nullptr; + + auto editable = EditableMapObject::find(mapObject); + if (editable) + return editable; - mDetachedMapObject.release(); + Q_ASSERT(mapObject->objectGroup()); + + editable = new EditableMapObject(asset, mapObject); + editable->moveOwnershipToCpp(); + return editable; +} + +void EditableMapObject::release(MapObject *mapObject) +{ + std::unique_ptr owned { mapObject }; + if (auto editable = EditableMapObject::find(mapObject)) + editable->hold(std::move(owned)); } void EditableMapObject::setShape(Shape shape) diff --git a/src/tiled/editablemapobject.h b/src/tiled/editablemapobject.h index 2d474baad3..13fe74fde7 100644 --- a/src/tiled/editablemapobject.h +++ b/src/tiled/editablemapobject.h @@ -139,9 +139,13 @@ class EditableMapObject : public EditableObject MapObject *mapObject() const; void detach(); - void attach(EditableMap *map); - void hold(); - void release(); + MapObject *attach(EditableAsset *asset); + void hold(std::unique_ptr mapObject); + bool isOwning() const; + + static EditableMapObject *find(MapObject *mapObject); + static EditableMapObject *get(EditableAsset *asset, MapObject *mapObject); + static void release(MapObject *mapObject); public slots: void setShape(Shape shape); @@ -277,6 +281,16 @@ inline MapObject *EditableMapObject::mapObject() const return static_cast(object()); } +inline bool EditableMapObject::isOwning() const +{ + return mDetachedMapObject.get() == object(); +} + +inline EditableMapObject *EditableMapObject::find(MapObject *mapObject) +{ + return static_cast(EditableObject::find(mapObject)); +} + inline void EditableMapObject::setX(qreal x) { setPos(QPointF(x, y())); diff --git a/src/tiled/editableobject.cpp b/src/tiled/editableobject.cpp index b9b5fc5795..d291b95e67 100644 --- a/src/tiled/editableobject.cpp +++ b/src/tiled/editableobject.cpp @@ -22,7 +22,6 @@ #include "changeproperties.h" #include "editableasset.h" -#include "editablemanager.h" #include "editablemapobject.h" #include "map.h" #include "mapobject.h" @@ -30,6 +29,7 @@ #include "scriptmanager.h" #include +#include namespace Tiled { @@ -40,6 +40,8 @@ EditableObject::EditableObject(EditableAsset *asset, , mAsset(asset) , mObject(object) { + if (object) + object->mEditable = this; } bool EditableObject::isReadOnly() const @@ -76,6 +78,20 @@ Document *EditableObject::document() const return asset() ? asset()->document() : nullptr; } +void EditableObject::setObject(Object *object) +{ + if (mObject == object) + return; + + if (mObject) + mObject->mEditable = nullptr; + + if (object) + object->mEditable = this; + + mObject = object; +} + void EditableObject::setClassName(const QString &className) { if (Document *doc = document()) @@ -84,6 +100,21 @@ void EditableObject::setClassName(const QString &className) object()->setClassName(className); } +bool EditableObject::moveOwnershipToJavaScript() +{ + // The object needs to be associated with a JS engine already + if (!qjsEngine(this)) + return false; + + QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); + return true; +} + +void EditableObject::moveOwnershipToCpp() +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +} + /** * When this object is read-only, raises a script error and returns true. */ @@ -143,7 +174,7 @@ QVariant EditableObject::toScript(const QVariant &value) const } if (referencedObject) { - auto editable = EditableManager::instance().editableMapObject(asset(), referencedObject); + auto editable = EditableMapObject::get(asset(), referencedObject); return QVariant::fromValue(editable); } } diff --git a/src/tiled/editableobject.h b/src/tiled/editableobject.h index a0caccaefc..b8aedd215f 100644 --- a/src/tiled/editableobject.h +++ b/src/tiled/editableobject.h @@ -77,9 +77,15 @@ class EditableObject : public QObject void setAsset(EditableAsset *asset); void setObject(Object *object); + static EditableObject *find(Object *object); + public slots: void setClassName(const QString &type); +protected: + bool moveOwnershipToJavaScript(); + void moveOwnershipToCpp(); + private: QVariant toScript(const QVariant &value) const; QVariant fromScript(const QVariant &value) const; @@ -146,9 +152,10 @@ inline void EditableObject::setAsset(EditableAsset *asset) mAsset = asset; } -inline void EditableObject::setObject(Object *object) +inline EditableObject *EditableObject::find(Object *object) { - mObject = object; + return object ? static_cast(object->mEditable.data()) + : nullptr; } } // namespace Tiled diff --git a/src/tiled/editableobjectgroup.cpp b/src/tiled/editableobjectgroup.cpp index a2c63cab39..d808ebe6a1 100644 --- a/src/tiled/editableobjectgroup.cpp +++ b/src/tiled/editableobjectgroup.cpp @@ -22,8 +22,8 @@ #include "addremovemapobject.h" #include "changeobjectgroupproperties.h" -#include "editablemanager.h" -#include "editablemap.h" +#include "editableasset.h" +#include "map.h" #include "scriptmanager.h" #include @@ -44,10 +44,9 @@ EditableObjectGroup::EditableObjectGroup(EditableAsset *asset, QList EditableObjectGroup::objects() { - auto &editableManager = EditableManager::instance(); QList objects; for (MapObject *object : objectGroup()->objects()) - objects.append(editableManager.editableMapObject(asset(), object)); + objects.append(EditableMapObject::get(asset(), object)); return objects; } @@ -59,7 +58,7 @@ EditableMapObject *EditableObjectGroup::objectAt(int index) } auto mapObject = objectGroup()->objectAt(index); - return EditableManager::instance().editableMapObject(asset(), mapObject); + return EditableMapObject::get(asset(), mapObject); } void EditableObjectGroup::removeObjectAt(int index) @@ -75,7 +74,7 @@ void EditableObjectGroup::removeObjectAt(int index) asset()->push(new RemoveMapObjects(doc, mapObject)); } else if (!checkReadOnly()) { objectGroup()->removeObjectAt(index); - EditableManager::instance().release(mapObject); + EditableMapObject::release(mapObject); } } @@ -105,9 +104,7 @@ void EditableObjectGroup::insertObjectAt(int index, EditableMapObject *editableM return; } - auto mapObject = editableMapObject->mapObject(); - - if (mapObject->objectGroup()) { + if (!editableMapObject->isOwning()) { ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Object already part of an object layer")); return; } @@ -115,6 +112,8 @@ void EditableObjectGroup::insertObjectAt(int index, EditableMapObject *editableM if (checkReadOnly()) return; + auto mapObject = editableMapObject->mapObject(); + // Avoid duplicate IDs by resetting when needed if (Map *map = objectGroup()->map()) { if (mapObject->id() != 0 && map->findObjectById(mapObject->id())) @@ -126,8 +125,8 @@ void EditableObjectGroup::insertObjectAt(int index, EditableMapObject *editableM entry.index = index; asset()->push(new AddMapObjects(doc, { entry })); } else { - objectGroup()->insertObject(index, mapObject); - editableMapObject->release(); // now owned by the object group + // ownership moves to the object group + objectGroup()->insertObject(index, editableMapObject->attach(asset())); } } @@ -136,6 +135,23 @@ void EditableObjectGroup::addObject(EditableMapObject *editableMapObject) insertObjectAt(objectCount(), editableMapObject); } +/** + * This functions exists in addition to EditableLayer::get() because the asset + * might also be an EditableTileset in the case of object groups. + */ +EditableObjectGroup *EditableObjectGroup::get(EditableAsset *asset, ObjectGroup *objectGroup) +{ + if (!objectGroup) + return nullptr; + + if (auto editable = EditableLayer::find(objectGroup)) + return static_cast(editable); + + auto editable = new EditableObjectGroup(asset, objectGroup); + editable->moveOwnershipToCpp(); + return editable; +} + void EditableObjectGroup::setColor(const QColor &color) { if (auto doc = document()) { diff --git a/src/tiled/editableobjectgroup.h b/src/tiled/editableobjectgroup.h index 073c322f22..c370807710 100644 --- a/src/tiled/editableobjectgroup.h +++ b/src/tiled/editableobjectgroup.h @@ -64,6 +64,8 @@ class EditableObjectGroup : public EditableLayer ObjectGroup *objectGroup() const; + static EditableObjectGroup *get(EditableAsset *asset, ObjectGroup *objectGroup); + public slots: void setColor(const QColor &color); void setDrawOrder(DrawOrder drawOrder); diff --git a/src/tiled/editabletile.cpp b/src/tiled/editabletile.cpp index c2503e8947..77d1d6af97 100644 --- a/src/tiled/editabletile.cpp +++ b/src/tiled/editabletile.cpp @@ -24,7 +24,6 @@ #include "changetileanimation.h" #include "changetileimagesource.h" #include "changetileobjectgroup.h" -#include "editablemanager.h" #include "editableobjectgroup.h" #include "editabletileset.h" #include "imagecache.h" @@ -44,7 +43,9 @@ EditableTile::EditableTile(EditableTileset *tileset, Tile *tile, QObject *parent EditableTile::~EditableTile() { - EditableManager::instance().remove(this); + // Prevent owned object from trying to delete us again + if (mDetachedTile) + setObject(nullptr); } EditableObjectGroup *EditableTile::objectGroup() const @@ -55,12 +56,14 @@ EditableObjectGroup *EditableTile::objectGroup() const Q_ASSERT(mAttachedObjectGroup == tile()->objectGroup()); } - return EditableManager::instance().editableObjectGroup(asset(), mAttachedObjectGroup); + return EditableObjectGroup::get(asset(), mAttachedObjectGroup); } QJSValue EditableTile::frames() const { - QJSEngine *engine = ScriptManager::instance().engine(); + QJSEngine *engine = qjsEngine(this); + if (!engine) + return QJSValue(); const auto &frames = tile()->frames(); QJSValue array = engine->newArray(frames.size()); @@ -94,22 +97,18 @@ void EditableTile::setImage(ScriptImage *image) void EditableTile::detach() { Q_ASSERT(tileset()); - - auto &editableManager = EditableManager::instance(); - - editableManager.remove(this); setAsset(nullptr); + if (!moveOwnershipToJavaScript()) + return; + mDetachedTile.reset(tile()->clone(nullptr)); setObject(mDetachedTile.get()); - editableManager.mEditables.insert(tile(), this); // Move over any attached editable object group - if (auto editable = editableManager.find(mAttachedObjectGroup)) { - editableManager.remove(editable); + if (auto editable = EditableLayer::find(mAttachedObjectGroup)) { editable->setAsset(nullptr); editable->setObject(tile()->objectGroup()); - editableManager.mEditables.insert(tile()->objectGroup(), editable); mAttachedObjectGroup = tile()->objectGroup(); } else { mAttachedObjectGroup = nullptr; @@ -120,17 +119,41 @@ void EditableTile::attach(EditableTileset *tileset) { Q_ASSERT(!asset() && tileset); + moveOwnershipToCpp(); setAsset(tileset); mDetachedTile.release(); } void EditableTile::detachObjectGroup() { - if (auto editable = EditableManager::instance().find(mAttachedObjectGroup)) + if (auto editable = EditableLayer::find(mAttachedObjectGroup)) editable->detach(); mAttachedObjectGroup = nullptr; } +EditableTile *EditableTile::get(Tile *tile) +{ + if (!tile) + return nullptr; + + auto tileset = EditableTileset::get(tile->tileset()); + return get(tileset, tile); +} + +EditableTile *EditableTile::get(EditableTileset *tileset, Tile *tile) +{ + Q_ASSERT(tile); + Q_ASSERT(tile->tileset() == tileset->tileset()); + + auto editable = EditableTile::find(tile); + if (editable) + return editable; + + editable = new EditableTile(tileset, tile); + editable->moveOwnershipToCpp(); + return editable; +} + void EditableTile::setImageFileName(const QString &fileName) { if (TilesetDocument *doc = tilesetDocument()) { @@ -182,7 +205,7 @@ void EditableTile::setObjectGroup(EditableObjectGroup *editableObjectGroup) return; } - og.reset(static_cast(editableObjectGroup->release())); + og.reset(static_cast(editableObjectGroup->attach(asset()))); } if (TilesetDocument *doc = tilesetDocument()) { diff --git a/src/tiled/editabletile.h b/src/tiled/editabletile.h index 424ee82086..897a69bc34 100644 --- a/src/tiled/editabletile.h +++ b/src/tiled/editabletile.h @@ -93,6 +93,10 @@ class EditableTile : public EditableObject const ObjectGroup *attachedObjectGroup() const { return mAttachedObjectGroup; } void detachObjectGroup(); + static EditableTile *find(Tile *tile); + static EditableTile *get(Tile *tile); + static EditableTile *get(EditableTileset *tileset, Tile *tile); + public slots: void setImageFileName(const QString &fileName); void setImageRect(const QRect &rect); @@ -153,6 +157,11 @@ inline Tile *EditableTile::tile() const return static_cast(object()); } +inline EditableTile *EditableTile::find(Tile *tile) +{ + return static_cast(EditableObject::find(tile)); +} + } // namespace Tiled Q_DECLARE_METATYPE(Tiled::EditableTile*) diff --git a/src/tiled/editabletilelayer.cpp b/src/tiled/editabletilelayer.cpp index 011fd3cb64..6178c234aa 100644 --- a/src/tiled/editabletilelayer.cpp +++ b/src/tiled/editabletilelayer.cpp @@ -22,7 +22,6 @@ #include "addremovetileset.h" #include "changelayer.h" -#include "editablemanager.h" #include "editablemap.h" #include "painttilelayer.h" #include "resizetilelayer.h" @@ -103,7 +102,7 @@ int EditableTileLayer::flagsAt(int x, int y) const EditableTile *EditableTileLayer::tileAt(int x, int y) const { - return EditableManager::instance().editableTile(cellAt(x, y).tile()); + return EditableTile::get(cellAt(x, y).tile()); } TileLayerEdit *EditableTileLayer::edit() @@ -126,7 +125,7 @@ TileLayerWangEdit *EditableTileLayer::wangEdit(EditableWangSet *wangSet) return new TileLayerWangEdit(this, wangSet); } -void EditableTileLayer::applyChangesFrom(TileLayer *changes, bool mergeable) +void EditableTileLayer::applyChangesFrom(TileLayer *changes, bool mergeable) { // Determine painted region and normalize the changes layer auto paintedRegion = changes->region([] (const Cell &cell) { return cell.checked(); }); diff --git a/src/tiled/editabletileset.cpp b/src/tiled/editabletileset.cpp index 4d6c4628f1..7ea471b098 100644 --- a/src/tiled/editabletileset.cpp +++ b/src/tiled/editabletileset.cpp @@ -22,7 +22,6 @@ #include "addremovetiles.h" #include "addremovewangset.h" -#include "editablemanager.h" #include "editabletile.h" #include "editablewangset.h" #include "scriptimage.h" @@ -40,7 +39,6 @@ EditableTileset::EditableTileset(const QString &name, QObject *parent) , mTileset(Tileset::create(name, 0, 0)) { setObject(mTileset.data()); - EditableManager::instance().mEditables.insert(tileset(), this); } EditableTileset::EditableTileset(const Tileset *tileset, QObject *parent) @@ -60,8 +58,6 @@ EditableTileset::EditableTileset(TilesetDocument *tilesetDocument, connect(tilesetDocument, &TilesetDocument::tileObjectGroupChanged, this, &EditableTileset::tileObjectGroupChanged); connect(tilesetDocument->wangSetModel(), &TilesetWangSetModel::wangSetAdded, this, &EditableTileset::wangSetAdded); connect(tilesetDocument->wangSetModel(), &TilesetWangSetModel::wangSetRemoved, this, &EditableTileset::wangSetRemoved); - - EditableManager::instance().mEditables.insert(tileset(), this); } EditableTileset::~EditableTileset() @@ -69,7 +65,9 @@ EditableTileset::~EditableTileset() detachTiles(tileset()->tiles()); detachWangSets(tileset()->wangSets()); - EditableManager::instance().remove(this); + // Prevent owned object from trying to delete us again + if (mTileset) + setObject(nullptr); } void EditableTileset::loadFromImage(ScriptImage *image, const QString &source) @@ -92,31 +90,29 @@ EditableTile *EditableTileset::tile(int id) return nullptr; } - return EditableManager::instance().editableTile(this, tile); + return EditableTile::get(this, tile); } EditableTile *EditableTileset::findTile(int id) { if (auto tile = tileset()->findTile(id)) - return EditableManager::instance().editableTile(this, tile); + return EditableTile::get(this, tile); return nullptr; } QList EditableTileset::tiles() { - auto &editableManager = EditableManager::instance(); QList tiles; for (Tile *tile : tileset()->tiles()) - tiles.append(editableManager.editableTile(this, tile)); + tiles.append(EditableTile::get(this, tile)); return tiles; } QList EditableTileset::wangSets() { - auto &editableManager = EditableManager::instance(); QList wangSets; for (WangSet *wangSet : tileset()->wangSets()) - wangSets.append(editableManager.editableWangSet(this, wangSet)); + wangSets.append(EditableWangSet::get(this, wangSet)); return wangSets; } @@ -127,9 +123,8 @@ QList EditableTileset::selectedTiles() QList selectedTiles; - auto &editableManager = EditableManager::instance(); for (Tile *tile : tilesetDocument()->selectedTiles()) - selectedTiles.append(editableManager.editableTile(this, tile)); + selectedTiles.append(EditableTile::get(this, tile)); return selectedTiles; } @@ -163,7 +158,7 @@ Tiled::EditableTile *EditableTileset::addTile() else tileset()->addTiles({ tile }); - return EditableManager::instance().editableTile(this, tile); + return EditableTile::get(this, tile); } void EditableTileset::removeTiles(const QList &tiles) @@ -197,7 +192,7 @@ EditableWangSet *EditableTileset::addWangSet(const QString &name, int type) else tileset()->addWangSet(std::move(wangSet)); - return EditableManager::instance().editableWangSet(this, tileset()->wangSets().last()); + return EditableWangSet::get(this, tileset()->wangSets().last()); } void EditableTileset::removeWangSet(EditableWangSet *editableWangSet) @@ -211,8 +206,7 @@ void EditableTileset::removeWangSet(EditableWangSet *editableWangSet) push(new RemoveWangSet(doc, editableWangSet->wangSet())); } else if (!checkReadOnly()) { const int index = tileset()->wangSets().indexOf(editableWangSet->wangSet()); - auto wangSet = tileset()->takeWangSetAt(index); - EditableManager::instance().release(std::move(wangSet)); + EditableWangSet::release(tileset()->takeWangSetAt(index)); } } @@ -226,6 +220,23 @@ QSharedPointer EditableTileset::createDocument() return TilesetDocumentPtr::create(mTileset); } +EditableTileset *EditableTileset::get(Tileset *tileset) +{ + if (!tileset) + return nullptr; + + if (auto document = TilesetDocument::findDocumentForTileset(tileset->sharedFromThis())) + return document->editable(); + + auto editable = EditableTileset::find(tileset); + if (editable) + return editable; + + editable = new EditableTileset(tileset); + editable->moveOwnershipToCpp(); + return editable; +} + void EditableTileset::setName(const QString &name) { if (auto doc = tilesetDocument()) @@ -371,18 +382,16 @@ bool EditableTileset::tilesFromEditables(const QList &editableTiles, void EditableTileset::attachTiles(const QList &tiles) { - const auto &editableManager = EditableManager::instance(); for (Tile *tile : tiles) { - if (EditableTile *editable = editableManager.find(tile)) + if (auto editable = EditableTile::find(tile)) editable->attach(this); } } void EditableTileset::detachTiles(const QList &tiles) { - const auto &editableManager = EditableManager::instance(); for (Tile *tile : tiles) { - if (EditableTile *editable = editableManager.find(tile)) { + if (auto editable = EditableTile::find(tile)) { Q_ASSERT(editable->tileset() == this); editable->detach(); } @@ -391,9 +400,8 @@ void EditableTileset::detachTiles(const QList &tiles) void EditableTileset::detachWangSets(const QList &wangSets) { - const auto &editableManager = EditableManager::instance(); for (WangSet *wangSet : wangSets) { - if (auto editable = editableManager.find(wangSet)) { + if (auto editable = EditableWangSet::find(wangSet)) { Q_ASSERT(editable->tileset() == this); editable->detach(); } @@ -404,7 +412,7 @@ void EditableTileset::tileObjectGroupChanged(Tile *tile) { Q_ASSERT(tile->tileset() == tileset()); - if (auto editable = EditableManager::instance().find(tile)) + if (auto editable = EditableTile::find(tile)) if (editable->attachedObjectGroup() != tile->objectGroup()) editable->detachObjectGroup(); } @@ -415,7 +423,7 @@ void EditableTileset::wangSetAdded(Tileset *tileset, int index) WangSet *wangSet = tileset->wangSet(index); - if (auto editable = EditableManager::instance().find(wangSet)) + if (auto editable = EditableWangSet::find(wangSet)) editable->attach(this); } diff --git a/src/tiled/editabletileset.h b/src/tiled/editabletileset.h index b82112cf76..3070eec6b1 100644 --- a/src/tiled/editabletileset.h +++ b/src/tiled/editabletileset.h @@ -150,6 +150,9 @@ class EditableTileset : public EditableAsset QSharedPointer createDocument() override; + static EditableTileset *find(Tileset *tileset); + static EditableTileset *get(Tileset *tileset); + public slots: void setName(const QString &name); void setImage(const QString &imageFilePath); @@ -298,6 +301,11 @@ inline Tileset *EditableTileset::tileset() const return static_cast(object()); } +inline EditableTileset *EditableTileset::find(Tileset *tileset) +{ + return static_cast(EditableObject::find(tileset)); +} + inline void EditableTileset::setTileWidth(int width) { setTileSize(QSize(width, tileHeight())); diff --git a/src/tiled/editablewangset.cpp b/src/tiled/editablewangset.cpp index f2de6bd4b6..3d010d2e72 100644 --- a/src/tiled/editablewangset.cpp +++ b/src/tiled/editablewangset.cpp @@ -23,7 +23,6 @@ #include "changetilewangid.h" #include "changewangcolordata.h" #include "changewangsetdata.h" -#include "editablemanager.h" #include "editabletile.h" #include "editabletileset.h" #include "scriptmanager.h" @@ -38,18 +37,19 @@ EditableWangSet::EditableWangSet(EditableTileset *tileset, QObject *parent) : EditableObject(tileset, wangSet, parent) { - } EditableWangSet::~EditableWangSet() { - EditableManager::instance().remove(this); + // Prevent owned object from trying to delete us again + if (mDetachedWangSet) + setObject(nullptr); } EditableTile *EditableWangSet::imageTile() const { if (Tile *tile = wangSet()->imageTile()) - return EditableManager::instance().editableTile(tileset(), tile); + return EditableTile::get(tileset(), tile); return nullptr; } @@ -66,7 +66,10 @@ QJSValue EditableWangSet::wangId(EditableTile *editableTile) return {}; } - QJSEngine *engine = ScriptManager::instance().engine(); + QJSEngine *engine = qjsEngine(this); + if (!engine) + return QJSValue(); + WangId wangId = wangSet()->wangIdOfTile(editableTile->tile()); QJSValue wangIdArray = engine->newArray(WangId::NumIndexes); @@ -176,38 +179,70 @@ void EditableWangSet::setColorCount(int n) void EditableWangSet::detach() { Q_ASSERT(tileset()); - - auto &editableManager = EditableManager::instance(); - - editableManager.remove(this); setAsset(nullptr); + if (!moveOwnershipToJavaScript()) + return; + mDetachedWangSet.reset(wangSet()->clone(nullptr)); setObject(mDetachedWangSet.get()); - editableManager.mEditables.insert(wangSet(), this); } void EditableWangSet::attach(EditableTileset *tileset) { Q_ASSERT(!asset() && tileset); + moveOwnershipToCpp(); setAsset(tileset); mDetachedWangSet.release(); } -void EditableWangSet::hold() +/** + * Take ownership of the referenced wang set or delete it. + */ +void EditableWangSet::hold(std::unique_ptr wangSet) { - Q_ASSERT(!asset()); // if asset exists, it holds the object (possibly indirectly) Q_ASSERT(!mDetachedWangSet); // can't already be holding the object + Q_ASSERT(this->wangSet() == wangSet.get()); + + if (!moveOwnershipToJavaScript()) + return; + + setAsset(nullptr); + mDetachedWangSet = std::move(wangSet); +} - mDetachedWangSet.reset(wangSet()); +EditableWangSet *EditableWangSet::get(WangSet *wangSet) +{ + if (!wangSet) + return nullptr; + + auto tileset = EditableTileset::get(wangSet->tileset()); + return get(tileset, wangSet); } -void EditableWangSet::release() +EditableWangSet *EditableWangSet::get(EditableTileset *tileset, WangSet *wangSet) { - Q_ASSERT(mDetachedWangSet.get() == wangSet()); + Q_ASSERT(wangSet); + Q_ASSERT(wangSet->tileset() == tileset->tileset()); - mDetachedWangSet.release(); + auto editable = EditableWangSet::find(wangSet); + if (editable) + return editable; + + editable = new EditableWangSet(tileset, wangSet); + editable->moveOwnershipToCpp(); + return editable; +} + +/** + * Releases the WangSet by either finding an EditableWangSet instance to take + * ownership of it or deleting it. + */ +void EditableWangSet::release(std::unique_ptr wangSet) +{ + if (auto editable = EditableWangSet::find(wangSet.get())) + editable->hold(std::move(wangSet)); } TilesetDocument *EditableWangSet::tilesetDocument() const diff --git a/src/tiled/editablewangset.h b/src/tiled/editablewangset.h index fc02723e5c..71da30dadb 100644 --- a/src/tiled/editablewangset.h +++ b/src/tiled/editablewangset.h @@ -77,8 +77,12 @@ class EditableWangSet : public EditableObject void detach(); void attach(EditableTileset *tileset); - void hold(); - void release(); + void hold(std::unique_ptr wangSet); + + static EditableWangSet *find(WangSet *wangSet); + static EditableWangSet *get(WangSet *wangSet); + static EditableWangSet *get(EditableTileset *tileset, WangSet *wangSet); + static void release(std::unique_ptr wangSet); private: TilesetDocument *tilesetDocument() const; @@ -112,6 +116,11 @@ inline WangSet *EditableWangSet::wangSet() const return static_cast(object()); } +inline EditableWangSet *EditableWangSet::find(WangSet *wangSet) +{ + return static_cast(EditableObject::find(wangSet)); +} + } // namespace Tiled Q_DECLARE_METATYPE(Tiled::EditableWangSet*) diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index df904169cb..99a43c440a 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -9,7 +9,7 @@ DynamicLibrary { Depends { name: "translations" } Depends { name: "qtpropertybrowser" } Depends { name: "qtsingleapplication" } - Depends { name: "Qt"; submodules: ["core", "widgets", "concurrent", "qml", "qml-private", "svg"]; versionAtLeast: "5.12" } + Depends { name: "Qt"; submodules: ["core", "widgets", "concurrent", "qml", "svg"]; versionAtLeast: "5.12" } Depends { name: "Qt.openglwidgets"; condition: Qt.core.versionMajor >= 6 } Depends { name: "Qt.dbus"; condition: qbs.targetOS.contains("linux") && project.dbus; required: false } Depends { name: "Qt.gui-private"; condition: qbs.targetOS.contains("windows") && Qt.core.versionMajor >= 6 } @@ -216,8 +216,6 @@ DynamicLibrary { "editableimagelayer.h", "editablelayer.cpp", "editablelayer.h", - "editablemanager.cpp", - "editablemanager.h", "editablemap.cpp", "editablemap.h", "editablemapobject.cpp", diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index fdeb0ffe50..200e4c0a12 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -32,7 +32,6 @@ #include "createtextobjecttool.h" #include "createtileobjecttool.h" #include "documentmanager.h" -#include "editablemanager.h" #include "editablemap.h" #include "editablewangset.h" #include "editpolygontool.h" @@ -1064,8 +1063,7 @@ void MapEditor::setCurrentBrush(EditableMap *editableMap) EditableWangSet *MapEditor::currentWangSet() const { - auto currentWangSet = mWangDock->currentWangSet(); - return EditableManager::instance().editableWangSet(currentWangSet); + return EditableWangSet::get(mWangDock->currentWangSet()); } int MapEditor::currentWangColorIndex() const diff --git a/src/tiled/reparentlayers.cpp b/src/tiled/reparentlayers.cpp index d391d25a0d..37bacbba75 100644 --- a/src/tiled/reparentlayers.cpp +++ b/src/tiled/reparentlayers.cpp @@ -23,7 +23,6 @@ #include "grouplayer.h" #include "layermodel.h" #include "mapdocument.h" -#include "map.h" namespace Tiled { diff --git a/src/tiled/scriptimage.cpp b/src/tiled/scriptimage.cpp index 941a36ac74..d92a4c1552 100644 --- a/src/tiled/scriptimage.cpp +++ b/src/tiled/scriptimage.cpp @@ -66,7 +66,10 @@ QByteArray ScriptImage::saveToData(const QByteArray &format, int quality) QJSValue ScriptImage::colorTable() const { - QJSEngine *engine = ScriptManager::instance().engine(); + QJSEngine *engine = qjsEngine(this); + if (!engine) + return QJSValue(); + const auto colors = mImage.colorTable(); QJSValue array = engine->newArray(colors.size()); diff --git a/src/tiled/scriptmanager.cpp b/src/tiled/scriptmanager.cpp index c1f15ea06c..2508aa3036 100644 --- a/src/tiled/scriptmanager.cpp +++ b/src/tiled/scriptmanager.cpp @@ -268,7 +268,7 @@ void ScriptManager::loadExtensions() QDir::setSearchPaths(QStringLiteral("ext"), extensionSearchPaths); - for (const QString &extensionPath : extensionSearchPaths) + for (const QString &extensionPath : std::as_const(extensionSearchPaths)) loadExtension(extensionPath); } diff --git a/src/tiled/tilecollisiondock.cpp b/src/tiled/tilecollisiondock.cpp index 580dac57df..5d50c29068 100644 --- a/src/tiled/tilecollisiondock.cpp +++ b/src/tiled/tilecollisiondock.cpp @@ -29,7 +29,6 @@ #include "createpolygonobjecttool.h" #include "createrectangleobjecttool.h" #include "createtemplatetool.h" -#include "editablemanager.h" #include "editablemapobject.h" #include "editpolygontool.h" #include "layermodel.h" @@ -307,7 +306,6 @@ QList TileCollisionDock::selectedObjectsForScript() const if (!mDummyMapDocument) return objects; - auto &editableManager = EditableManager::instance(); auto editableTileset = mTilesetDocument->editable(); const auto &originalObjects = mTile->objectGroup()->objects(); @@ -318,7 +316,7 @@ QList TileCollisionDock::selectedObjectsForScript() const if (it != originalObjects.end()) { MapObject *oo = *it; - objects.append(editableManager.editableMapObject(editableTileset, oo)); + objects.append(EditableMapObject::get(editableTileset, oo)); } } diff --git a/src/tiled/tilesetdock.cpp b/src/tiled/tilesetdock.cpp index ff4eb196d3..8ac0db0aa0 100644 --- a/src/tiled/tilesetdock.cpp +++ b/src/tiled/tilesetdock.cpp @@ -27,7 +27,6 @@ #include "addremovetileset.h" #include "containerhelpers.h" #include "documentmanager.h" -#include "editablemanager.h" #include "editabletile.h" #include "erasetiles.h" #include "map.h" @@ -1033,10 +1032,9 @@ QList TilesetDock::selectedTiles() const EditableTileset *editableTileset = currentEditableTileset(); const TilesetModel *model = view->tilesetModel(); - auto &editableManager = EditableManager::instance(); for (const QModelIndex &index : indexes) if (Tile *tile = model->tileAt(index)) - result.append(editableManager.editableTile(editableTileset, tile)); + result.append(EditableTile::get(editableTileset, tile)); return result; } diff --git a/src/tiled/tilesetdocument.cpp b/src/tiled/tilesetdocument.cpp index 89e4ec4250..3f5123576b 100644 --- a/src/tiled/tilesetdocument.cpp +++ b/src/tiled/tilesetdocument.cpp @@ -21,7 +21,6 @@ #include "tilesetdocument.h" #include "changeevents.h" -#include "editablemanager.h" #include "editabletileset.h" #include "issuesmodel.h" #include "map.h" @@ -70,7 +69,7 @@ TilesetDocument::TilesetDocument(const SharedTileset &tileset) // If there already happens to be an editable for this tileset, take // ownership of it. - if (auto editable = EditableManager::instance().find(tileset.data())) { + if (auto editable = EditableTileset::find(tileset.data())) { setEditable(std::unique_ptr(editable)); QQmlEngine::setObjectOwnership(editable, QQmlEngine::CppOwnership); } diff --git a/src/tiled/tileseteditor.cpp b/src/tiled/tileseteditor.cpp index 794dd1ea10..592831fd65 100644 --- a/src/tiled/tileseteditor.cpp +++ b/src/tiled/tileseteditor.cpp @@ -27,7 +27,6 @@ #include "changewangcolordata.h" #include "changewangsetdata.h" #include "documentmanager.h" -#include "editablemanager.h" #include "editablewangset.h" #include "erasetiles.h" #include "imagecache.h" @@ -506,8 +505,7 @@ QAction *TilesetEditor::editWangSetsAction() const EditableWangSet *TilesetEditor::currentWangSet() const { - auto currentWangSet = mWangDock->currentWangSet(); - return EditableManager::instance().editableWangSet(currentWangSet); + return EditableWangSet::get(mWangDock->currentWangSet()); } int TilesetEditor::currentWangColorIndex() const