Skip to content

Commit

Permalink
Allow changing the image source of individual tiles
Browse files Browse the repository at this point in the history
This only applies to tiles in an image collection tileset. Broken
references to individual tile images are now no longer raised as an XML
parsing error.

Also, an "image-missing" icon was introduced which shows in the tileset
view for tiles which images could not be loaded, and in the properties
view for when the referenced file isn't found.

Issue #913
  • Loading branch information
bjorn committed Oct 26, 2015
1 parent e378c16 commit a80fca2
Show file tree
Hide file tree
Showing 20 changed files with 280 additions and 45 deletions.
2 changes: 0 additions & 2 deletions src/libtiled/mapreader.cpp
Expand Up @@ -416,8 +416,6 @@ void MapReaderPrivate::readTilesetTile(SharedTileset &tileset)
if (image.isNull()) {
if (imageReference.source.isEmpty())
xml.raiseError(tr("Error reading embedded image for tile %1").arg(id));
else
xml.raiseError(tr("Error loading image:\n'%1'").arg(imageReference.source));
}
tileset->setTileImage(id, QPixmap::fromImage(image),
imageReference.source);
Expand Down
53 changes: 53 additions & 0 deletions src/tiled/changetileimagesource.cpp
@@ -0,0 +1,53 @@
/*
* changetileimagesource.cpp
* Copyright 2015, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "changetileimagesource.h"

#include "mapdocument.h"
#include "tile.h"

#include <QCoreApplication>

namespace Tiled {
namespace Internal {

ChangeTileImageSource::ChangeTileImageSource(MapDocument *mapDocument,
Tile *tile,
const QString &imageSource)
: mMapDocument(mapDocument)
, mTile(tile)
, mOldImageSource(tile->imageSource())
, mNewImageSource(imageSource)
{
setText(QCoreApplication::translate("Undo Commands",
"Change Tile Image"));
}

void ChangeTileImageSource::apply(const QString &imageSource)
{
mTile->tileset()->setTileImage(mTile->id(),
QPixmap(imageSource),
imageSource);

emit mMapDocument->tileImageSourceChanged(mTile);
}

} // namespace Internal
} // namespace Tiled
56 changes: 56 additions & 0 deletions src/tiled/changetileimagesource.h
@@ -0,0 +1,56 @@
/*
* changetileimagesource.h
* Copyright 2015, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef TILED_INTERNAL_CHANGETILEIMAGESOURCE_H
#define TILED_INTERNAL_CHANGETILEIMAGESOURCE_H

#include <QUndoCommand>

namespace Tiled {

class Tile;

namespace Internal {

class MapDocument;

class ChangeTileImageSource : public QUndoCommand
{
public:
ChangeTileImageSource(MapDocument *mapDocument,
Tile *tile,
const QString &imageSource);

void undo() { apply(mOldImageSource); }
void redo() { apply(mNewImageSource); }

private:
void apply(const QString &imageSource);

MapDocument *mMapDocument;
Tile *mTile;
QString mOldImageSource;
QString mNewImageSource;
};

} // namespace Internal
} // namespace Tiled

#endif // TILED_INTERNAL_CHANGETILEIMAGESOURCE_H
Binary file added src/tiled/images/16x16/image-missing.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/tiled/images/32x32/image-missing.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/tiled/mapdocument.h
Expand Up @@ -342,6 +342,7 @@ class MapDocument : public QObject

void tileLayerDrawMarginsChanged(TileLayer *layer);

void tileImageSourceChanged(Tile *tile);
void tileTerrainChanged(const QList<Tile*> &tiles);
void tileProbabilityChanged(Tile *tile);
void tileObjectGroupChanged(Tile *tile);
Expand Down
13 changes: 13 additions & 0 deletions src/tiled/mapscene.cpp
Expand Up @@ -144,6 +144,8 @@ void MapScene::setMapDocument(MapDocument *mapDocument)
this, SLOT(currentLayerIndexChanged()));
connect(mMapDocument, SIGNAL(tilesetTileOffsetChanged(Tileset*)),
this, SLOT(tilesetTileOffsetChanged(Tileset*)));
connect(mMapDocument, &MapDocument::tileImageSourceChanged,
this, &MapScene::tileImageSourceChanged);
connect(mMapDocument, SIGNAL(objectsInserted(ObjectGroup*,int,int)),
this, SLOT(objectsInserted(ObjectGroup*,int,int)));
connect(mMapDocument, SIGNAL(objectsRemoved(QList<MapObject*>)),
Expand Down Expand Up @@ -478,6 +480,17 @@ void MapScene::tilesetTileOffsetChanged(Tileset *tileset)
}
}

void MapScene::tileImageSourceChanged(Tile *tile)
{
update();

for (MapObjectItem *item : mObjectItems) {
const Cell &cell = item->mapObject()->cell();
if (cell.tile == tile)
item->syncWithMapObject();
}
}

/**
* Inserts map object items for the given objects.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/mapscene.h
Expand Up @@ -35,6 +35,7 @@ class ImageLayer;
class Layer;
class MapObject;
class ObjectGroup;
class Tile;
class TileLayer;
class Tileset;

Expand Down Expand Up @@ -165,6 +166,7 @@ private slots:
void imageLayerChanged(ImageLayer *imageLayer);

void tilesetTileOffsetChanged(Tileset *tileset);
void tileImageSourceChanged(Tile *tile);

void objectsInserted(ObjectGroup *objectGroup, int first, int last);
void objectsRemoved(const QList<MapObject*> &objects);
Expand Down
38 changes: 33 additions & 5 deletions src/tiled/propertybrowser.cpp
Expand Up @@ -27,6 +27,7 @@
#include "changemapproperty.h"
#include "changeobjectgroupproperties.h"
#include "changeproperties.h"
#include "changetileimagesource.h"
#include "changetileprobability.h"
#include "flipmapobjects.h"
#include "imagelayer.h"
Expand Down Expand Up @@ -144,8 +145,10 @@ void PropertyBrowser::setMapDocument(MapDocument *mapDocument)
connect(mapDocument, &MapDocument::tilesetChanged,
this, &PropertyBrowser::tilesetChanged);

connect(mapDocument, SIGNAL(tileProbabilityChanged(Tile*)),
SLOT(tileChanged(Tile*)));
connect(mapDocument, &MapDocument::tileProbabilityChanged,
this, &PropertyBrowser::tileChanged);
connect(mapDocument, &MapDocument::tileImageSourceChanged,
this, &PropertyBrowser::tileChanged);

TerrainModel *terrainModel = mapDocument->terrainModel();
connect(terrainModel, SIGNAL(terrainChanged(Tileset*,int)),
Expand Down Expand Up @@ -507,7 +510,7 @@ void PropertyBrowser::addTilesetProperties()
createProperty(TileOffsetProperty, QVariant::Point, tr("Drawing Offset"), groupProperty);

// Next properties we should add only for non 'Collection of Images' tilesets
const Tileset *currentTileset = dynamic_cast<const Tileset*>(mObject);
const Tileset *currentTileset = static_cast<const Tileset*>(mObject);
if (!currentTileset->imageSource().isEmpty()) {
QtVariantProperty *parametersProperty =
createProperty(TilesetImageParametersProperty, VariantPropertyManager::tilesetParametersTypeId(), tr("Image"), groupProperty);
Expand Down Expand Up @@ -535,6 +538,8 @@ void PropertyBrowser::addTileProperties()
{
QtProperty *groupProperty = mGroupManager->addProperty(tr("Tile"));
createProperty(IdProperty, QVariant::Int, tr("ID"), groupProperty)->setEnabled(false);
createProperty(WidthProperty, QVariant::Int, tr("Width"), groupProperty)->setEnabled(false);
createProperty(HeightProperty, QVariant::Int, tr("Height"), groupProperty)->setEnabled(false);

QtVariantProperty *probabilityProperty = createProperty(TileProbabilityProperty,
QVariant::Double,
Expand All @@ -543,6 +548,16 @@ void PropertyBrowser::addTileProperties()
probabilityProperty->setAttribute(QLatin1String("decimals"), 3);
probabilityProperty->setToolTip(tr("Relative chance this tile will be picked"));

const Tile *tile = static_cast<const Tile*>(mObject);
if (!tile->imageSource().isEmpty()) {
QtVariantProperty *imageSourceProperty = createProperty(ImageSourceProperty,
VariantPropertyManager::filePathTypeId(),
tr("Image"), groupProperty);

imageSourceProperty->setAttribute(QLatin1String("filter"),
Utils::readableImageFormatsFilter());
}

addProperty(groupProperty);
}

Expand Down Expand Up @@ -846,11 +861,19 @@ void PropertyBrowser::applyTilesetValue(PropertyBrowser::PropertyId id, const QV
void PropertyBrowser::applyTileValue(PropertyId id, const QVariant &val)
{
Tile *tile = static_cast<Tile*>(mObject);
QUndoStack *undoStack = mMapDocument->undoStack();

if (id == TileProbabilityProperty) {
QUndoStack *undoStack = mMapDocument->undoStack();
switch (id) {
case TileProbabilityProperty:
undoStack->push(new ChangeTileProbability(mMapDocument,
tile, val.toFloat()));
break;
case ImageSourceProperty:
undoStack->push(new ChangeTileImageSource(mMapDocument,
tile, val.toString()));
break;
default:
break;
}
}

Expand Down Expand Up @@ -1032,8 +1055,13 @@ void PropertyBrowser::updateProperties()
}
case Object::TileType: {
const Tile *tile = static_cast<const Tile*>(mObject);
const QSize tileSize = tile->size();
mIdToProperty[IdProperty]->setValue(tile->id());
mIdToProperty[WidthProperty]->setValue(tileSize.width());
mIdToProperty[HeightProperty]->setValue(tileSize.height());
mIdToProperty[TileProbabilityProperty]->setValue(tile->probability());
if (QtVariantProperty *imageSourceProperty = mIdToProperty.value(ImageSourceProperty))
imageSourceProperty->setValue(tile->imageSource());
break;
}
case Object::TerrainType: {
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tiled.pro
Expand Up @@ -64,6 +64,7 @@ SOURCES += aboutdialog.cpp \
changepolygon.cpp \
changeproperties.cpp \
changetileanimation.cpp \
changetileimagesource.cpp \
changetileobjectgroup.cpp \
changetileprobability.cpp \
changeselectedarea.cpp \
Expand Down Expand Up @@ -202,6 +203,7 @@ HEADERS += aboutdialog.h \
changepolygon.h \
changeproperties.h \
changetileanimation.h \
changetileimagesource.h \
changetileobjectgroup.h \
changetileprobability.h \
changeselectedarea.h \
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tiled.qbs
Expand Up @@ -80,6 +80,8 @@ QtGuiApplication {
"changeselectedarea.h",
"changetileanimation.cpp",
"changetileanimation.h",
"changetileimagesource.cpp",
"changetileimagesource.h",
"changetileobjectgroup.cpp",
"changetileobjectgroup.h",
"changetileprobability.cpp",
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tiled.qrc
Expand Up @@ -81,5 +81,7 @@
<file>images/24x24/move-image-layer.png</file>
<file>images/16x16/rename.png</file>
<file>images/22x22/stock-tool-fuzzy-select-22.png</file>
<file>images/16x16/image-missing.png</file>
<file>images/32x32/image-missing.png</file>
</qresource>
</RCC>
40 changes: 26 additions & 14 deletions src/tiled/tilesetdock.cpp
Expand Up @@ -353,20 +353,22 @@ void TilesetDock::setMapDocument(MapDocument *mapDocument)
mViewStack->addWidget(view);
}

connect(mMapDocument, SIGNAL(tilesetAdded(int,Tileset*)),
SLOT(tilesetAdded(int,Tileset*)));
connect(mMapDocument, SIGNAL(tilesetRemoved(Tileset*)),
SLOT(tilesetRemoved(Tileset*)));
connect(mMapDocument, SIGNAL(tilesetMoved(int,int)),
SLOT(tilesetMoved(int,int)));
connect(mMapDocument, SIGNAL(tilesetNameChanged(Tileset*)),
SLOT(tilesetNameChanged(Tileset*)));
connect(mMapDocument, SIGNAL(tilesetFileNameChanged(Tileset*)),
SLOT(updateActions()));
connect(mMapDocument, SIGNAL(tilesetChanged(Tileset*)),
SLOT(tilesetChanged(Tileset*)));
connect(mMapDocument, SIGNAL(tileAnimationChanged(Tile*)),
SLOT(tileAnimationChanged(Tile*)));
connect(mMapDocument, &MapDocument::tilesetAdded,
this, &TilesetDock::tilesetAdded);
connect(mMapDocument, &MapDocument::tilesetRemoved,
this, &TilesetDock::tilesetRemoved);
connect(mMapDocument, &MapDocument::tilesetMoved,
this, &TilesetDock::tilesetMoved);
connect(mMapDocument, &MapDocument::tilesetNameChanged,
this, &TilesetDock::tilesetNameChanged);
connect(mMapDocument, &MapDocument::tilesetFileNameChanged,
this, &TilesetDock::updateActions);
connect(mMapDocument, &MapDocument::tilesetChanged,
this, &TilesetDock::tilesetChanged);
connect(mMapDocument, &MapDocument::tileImageSourceChanged,
this, &TilesetDock::tileImageSourceChanged);
connect(mMapDocument, &MapDocument::tileAnimationChanged,
this, &TilesetDock::tileAnimationChanged);

QString cacheName = mCurrentTilesets.take(mMapDocument);
for (int i = 0; i < mTabBar->count(); ++i) {
Expand Down Expand Up @@ -1021,6 +1023,16 @@ void TilesetDock::tilesetNameChanged(Tileset *tileset)
mTabBar->setTabText(index, tileset->name());
}

void TilesetDock::tileImageSourceChanged(Tile *tile)
{
int tilesetIndex = mTilesets.indexOf(tile->tileset()->sharedPointer());
if (tilesetIndex != -1) {
TilesetView *view = tilesetViewAt(tilesetIndex);
if (TilesetModel *model = view->tilesetModel())
model->tileChanged(tile);
}
}

void TilesetDock::tileAnimationChanged(Tile *tile)
{
if (TilesetView *view = currentTilesetView())
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tilesetdock.h
Expand Up @@ -120,6 +120,8 @@ private slots:
void tilesetRemoved(Tileset *tileset);
void tilesetMoved(int from, int to);
void tilesetNameChanged(Tileset *tileset);

void tileImageSourceChanged(Tile *tile);
void tileAnimationChanged(Tile *tile);

void removeTileset();
Expand Down
4 changes: 2 additions & 2 deletions src/tiled/tilesetmodel.cpp
Expand Up @@ -112,7 +112,7 @@ QMimeData *TilesetModel::mimeData(const QModelIndexList &indexes) const

QDataStream stream(&encodedData, QIODevice::WriteOnly);

foreach (const QModelIndex &index, indexes) {
for (const QModelIndex &index : indexes) {
if (index.isValid())
stream << tileIndexAt(index);
}
Expand Down Expand Up @@ -172,7 +172,7 @@ void TilesetModel::tilesChanged(const QList<Tile *> &tiles)
QModelIndex topLeft;
QModelIndex bottomRight;

foreach (const Tile *tile, tiles) {
for (const Tile *tile : tiles) {
const QModelIndex i = tileIndex(tile);

if (!topLeft.isValid()) {
Expand Down

0 comments on commit a80fca2

Please sign in to comment.