diff --git a/NEWS.md b/NEWS.md index 7e16f8d502..18255fc69f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ * JSON format: Fixed tile order when loading a tileset using the old format * tmxrasterizer: Added --hide-object and --show-object arguments (by Lars Luz, #3819) +* Scripting: Made Tileset.margin and Tileset.tileSpacing writable * Windows: Fixed the support for WebP images (updated to Qt 6.5.3) ### Tiled 1.10.2 (4 August 2023) diff --git a/docs/scripting-doc/index.d.ts b/docs/scripting-doc/index.d.ts index 002c8e369d..194439b0c6 100644 --- a/docs/scripting-doc/index.d.ts +++ b/docs/scripting-doc/index.d.ts @@ -3336,13 +3336,21 @@ declare class Tileset extends Asset { /** * Spacing between tiles in this tileset in pixels. + * + * @note Changing this property will cause an image-based tileset to update + * all its tiles. When setting up a tileset, you'll want to set this property + * before setting the {@link image} property. */ - readonly tileSpacing : number + tileSpacing : number /** * Margin around the tileset in pixels (only used at the top and left sides of the tileset image). + * + * @note Changing this property will cause an image-based tileset to update + * all its tiles. When setting up a tileset, you'll want to set this property + * before setting the {@link image} property. */ - readonly margin : number + margin : number /** * The alignment to use for tile objects (when Unspecified, uses Bottom alignment on isometric maps and BottomLeft alignment for all other maps). diff --git a/src/libtiled/imagereference.cpp b/src/libtiled/imagereference.cpp index 630de97dca..164c772f42 100644 --- a/src/libtiled/imagereference.cpp +++ b/src/libtiled/imagereference.cpp @@ -21,7 +21,6 @@ #include "imagereference.h" #include "imagecache.h" -#include "tileset.h" namespace Tiled { @@ -33,10 +32,10 @@ bool ImageReference::hasImage() const QPixmap ImageReference::create() const { QPixmap pixmap; - if (source.isLocalFile()) - pixmap = ImageCache::loadPixmap(source.toLocalFile()); - else if (source.scheme() == QLatin1String("qrc")) - pixmap = ImageCache::loadPixmap(QLatin1Char(':') + source.path()); + + const QString fileName = Tiled::urlToLocalFileOrQrc(source); + if (!fileName.isEmpty()) + pixmap = ImageCache::loadPixmap(fileName); else if (!data.isEmpty()) pixmap = QPixmap::fromImage(QImage::fromData(data, format)); diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index 6dbb9c0ce1..820eed8b43 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -307,9 +307,8 @@ std::unique_ptr MapReaderPrivate::readMap() mMap.reset(); } else { // Try to load the tileset images for embedded tilesets - auto tilesets = mMap->tilesets(); - for (SharedTileset &tileset : tilesets) { - if (!tileset->isCollection() && tileset->fileName().isEmpty()) + for (const SharedTileset &tileset : mMap->tilesets()) { + if (tileset->fileName().isEmpty()) tileset->loadImage(); } @@ -1501,7 +1500,7 @@ std::unique_ptr MapReader::readMap(const QString &fileName) SharedTileset MapReader::readTileset(QIODevice *device, const QString &path) { SharedTileset tileset = d->readTileset(device, path); - if (tileset && !tileset->isCollection()) + if (tileset) tileset->loadImage(); return tileset; diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index 63758d2208..2025c5b14a 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -404,12 +404,17 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset &tileset, writeProperties(w, tileset.properties()); // Write the image element - const QUrl &imageSource = tileset.imageSource(); - if (!imageSource.isEmpty()) { + const bool isCollection = tileset.isCollection(); + if (!isCollection) { w.writeStartElement(QStringLiteral("image")); - QString source = toFileReference(imageSource, mUseAbsolutePaths ? QString() - : mDir.path()); - w.writeAttribute(QStringLiteral("source"), source); + + const QUrl &imageSource = tileset.imageSource(); + if (!imageSource.isEmpty()) { + // Write a reference to an external tileset image + QString source = toFileReference(imageSource, mUseAbsolutePaths ? QString() + : mDir.path()); + w.writeAttribute(QStringLiteral("source"), source); + } const QColor transColor = tileset.transparentColor(); if (transColor.isValid()) @@ -422,10 +427,22 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset &tileset, w.writeAttribute(QStringLiteral("height"), QString::number(tileset.imageHeight())); + if (imageSource.isEmpty()) { + // Write an embedded image + w.writeAttribute(QStringLiteral("format"), QLatin1String("png")); + + w.writeStartElement(QStringLiteral("data")); + w.writeAttribute(QStringLiteral("encoding"), QLatin1String("base64")); + + QBuffer buffer; + tileset.image().save(&buffer, "png"); + w.writeCharacters(QString::fromLatin1(buffer.data().toBase64())); + w.writeEndElement(); // + } + w.writeEndElement(); } - const bool isCollection = tileset.isCollection(); const bool includeAllTiles = isCollection || tileset.anyTileOutOfOrder(); for (const Tile *tile : tileset.tiles()) { diff --git a/src/libtiled/tileset.cpp b/src/libtiled/tileset.cpp index ea27a7b9f8..0f2564b52e 100644 --- a/src/libtiled/tileset.cpp +++ b/src/libtiled/tileset.cpp @@ -228,31 +228,29 @@ bool Tileset::loadFromImage(const QString &fileName) } /** - * Tries to load the image this tileset is referring to. + * Tries to load the image this tileset is referring to, if any. * * @return true if loading was successful, otherwise * returns false */ bool Tileset::loadImage() { - if (mTileWidth <= 0 || mTileHeight <= 0) { - mImageReference.status = LoadingError; - return false; - } - - mImage = ImageCache::loadPixmap(Tiled::urlToLocalFileOrQrc(mImageReference.source)); - if (mImage.isNull()) { - mImageReference.status = LoadingError; - return false; + if (mImageReference.hasImage()) { + mImage = mImageReference.create(); + if (mImage.isNull()) { + mImageReference.status = LoadingError; + return false; + } } - initializeTilesetTiles(); - - return true; + return initializeTilesetTiles(); } -void Tileset::initializeTilesetTiles() +bool Tileset::initializeTilesetTiles() { + if (mImage.isNull() || mTileWidth <= 0 || mTileHeight <= 0) + return false; + if (mImageReference.transparentColor.isValid()) mImage.setMask(mImage.createMaskFromColor(mImageReference.transparentColor)); @@ -294,6 +292,7 @@ void Tileset::initializeTilesetTiles() mImageReference.size = mImage.size(); mColumnCount = columnCountForWidth(mImageReference.size.width()); mImageReference.status = LoadingReady; + return true; } /** diff --git a/src/libtiled/tileset.h b/src/libtiled/tileset.h index d00602a4c6..c7b5b01156 100644 --- a/src/libtiled/tileset.h +++ b/src/libtiled/tileset.h @@ -198,6 +198,7 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThis &tilesets) const; @@ -293,8 +294,6 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThis &Tileset::wangSets() const diff --git a/src/libtiled/tilesetmanager.cpp b/src/libtiled/tilesetmanager.cpp index 11f3a578f9..fa97cdcef3 100644 --- a/src/libtiled/tilesetmanager.cpp +++ b/src/libtiled/tilesetmanager.cpp @@ -151,7 +151,7 @@ void TilesetManager::reloadImages(Tileset *tileset) } } emit tilesetImagesChanged(tileset); - } else { + } else if (tileset->imageSource().isLocalFile()) { ImageCache::remove(tileset->imageSource().toLocalFile()); if (tileset->loadImage()) emit tilesetImagesChanged(tileset); diff --git a/src/libtiled/varianttomapconverter.cpp b/src/libtiled/varianttomapconverter.cpp index 1ed449a227..2c2b9bf3b5 100644 --- a/src/libtiled/varianttomapconverter.cpp +++ b/src/libtiled/varianttomapconverter.cpp @@ -125,9 +125,8 @@ std::unique_ptr VariantToMapConverter::toMap(const QVariant &variant, } // Try to load the tileset images - auto tilesets = map->tilesets(); - for (SharedTileset &tileset : tilesets) { - if (!tileset->imageSource().isEmpty() && tileset->fileName().isEmpty()) + for (const SharedTileset &tileset : map->tilesets()) { + if (tileset->fileName().isEmpty()) tileset->loadImage(); } @@ -145,7 +144,7 @@ SharedTileset VariantToMapConverter::toTileset(const QVariant &variant, mReadingExternalTileset = true; SharedTileset tileset = toTileset(variant); - if (tileset && !tileset->imageSource().isEmpty()) + if (tileset) tileset->loadImage(); mReadingExternalTileset = false; diff --git a/src/tiled/editabletileset.cpp b/src/tiled/editabletileset.cpp index 4d6c4628f1..f5d05b1d9a 100644 --- a/src/tiled/editabletileset.cpp +++ b/src/tiled/editabletileset.cpp @@ -248,9 +248,7 @@ void EditableTileset::setImage(const QString &imageFilePath) push(new ChangeTilesetParameters(doc, parameters)); } else if (!checkReadOnly()) { tileset()->setImageSource(imageFilePath); - - if (!tileSize().isEmpty() && !image().isEmpty()) - tileset()->loadImage(); + tileset()->loadImage(); } } @@ -268,9 +266,43 @@ void EditableTileset::setTileSize(QSize size) push(new ChangeTilesetParameters(doc, parameters)); } else if (!checkReadOnly()) { tileset()->setTileSize(size); + tileset()->initializeTilesetTiles(); + } +} + +void EditableTileset::setTileSpacing(int tileSpacing) +{ + if (isCollection() && tileCount() > 0) { + ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Can't set tile spacing on an image collection tileset")); + return; + } + + if (auto doc = tilesetDocument()) { + TilesetParameters parameters(*tileset()); + parameters.tileSpacing = tileSpacing; - if (!tileSize().isEmpty() && !image().isEmpty()) - tileset()->loadImage(); + push(new ChangeTilesetParameters(doc, parameters)); + } else if (!checkReadOnly()) { + tileset()->setTileSpacing(tileSpacing); + tileset()->initializeTilesetTiles(); + } +} + +void EditableTileset::setMargin(int margin) +{ + if (isCollection() && tileCount() > 0) { + ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Can't set margin on an image collection tileset")); + return; + } + + if (auto doc = tilesetDocument()) { + TilesetParameters parameters(*tileset()); + parameters.margin = margin; + + push(new ChangeTilesetParameters(doc, parameters)); + } else if (!checkReadOnly()) { + tileset()->setMargin(margin); + tileset()->initializeTilesetTiles(); } } @@ -336,9 +368,7 @@ void EditableTileset::setTransparentColor(const QColor &color) push(new ChangeTilesetParameters(doc, parameters)); } else if (!checkReadOnly()) { tileset()->setTransparentColor(color); - - if (!tileSize().isEmpty() && !image().isEmpty()) - tileset()->loadImage(); + tileset()->initializeTilesetTiles(); } } diff --git a/src/tiled/editabletileset.h b/src/tiled/editabletileset.h index b82112cf76..8db0a30fe8 100644 --- a/src/tiled/editabletileset.h +++ b/src/tiled/editabletileset.h @@ -47,8 +47,8 @@ class EditableTileset : public EditableAsset Q_PROPERTY(int imageWidth READ imageWidth) Q_PROPERTY(int imageHeight READ imageHeight) Q_PROPERTY(QSize imageSize READ imageSize) - Q_PROPERTY(int tileSpacing READ tileSpacing) - Q_PROPERTY(int margin READ margin) + Q_PROPERTY(int tileSpacing READ tileSpacing WRITE setTileSpacing) + Q_PROPERTY(int margin READ margin WRITE setMargin) Q_PROPERTY(Alignment objectAlignment READ objectAlignment WRITE setObjectAlignment) Q_PROPERTY(TileRenderSize tileRenderSize READ tileRenderSize WRITE setTileRenderSize) Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode) @@ -157,6 +157,8 @@ public slots: void setTileHeight(int height); void setTileSize(QSize size); void setTileSize(int width, int height); + void setTileSpacing(int tileSpacing); + void setMargin(int margin); void setColumnCount(int columnCount); void setObjectAlignment(Alignment objectAlignment); void setTileRenderSize(TileRenderSize tileRenderSize);