Skip to content

Commit

Permalink
AutoMapping: Support map name filters in rules.txt
Browse files Browse the repository at this point in the history
Closes #3014
  • Loading branch information
bjorn committed Apr 5, 2022
1 parent 866c176 commit cd635c5
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 30 deletions.
3 changes: 2 additions & 1 deletion src/tiled/automapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ AutoMappingContext::AutoMappingContext(MapDocument *mapDocument)
* are put directly below each of these functions.
*/

AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap)
AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter)
: mRulesMap(std::move(rulesMap))
, mMapNameFilter(mapNameFilter)
{
Q_ASSERT(mRulesMap);

Expand Down
12 changes: 10 additions & 2 deletions src/tiled/automapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <QList>
#include <QMap>
#include <QRegion>
#include <QRegularExpression>
#include <QSet>
#include <QString>
#include <QVector>
Expand Down Expand Up @@ -235,10 +236,11 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
* @param rulesMap The map containing the AutoMapping rules. The
* AutoMapper takes ownership of this map.
*/
AutoMapper(std::unique_ptr<Map> rulesMap);
AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter = {});
~AutoMapper() override;

QString rulesMapFileName() const;
const QRegularExpression &mapNameFilter() const;

/**
* Checks if the passed \a ruleLayerName is used as input layer in this
Expand Down Expand Up @@ -374,7 +376,8 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
/**
* Map containing the rules.
*/
std::unique_ptr<Map> mRulesMap;
const std::unique_ptr<Map> mRulesMap;
const QRegularExpression mMapNameFilter;

RuleMapSetup mRuleMapSetup;

Expand All @@ -389,4 +392,9 @@ class TILED_EDITOR_EXPORT AutoMapper : public QObject
QString mWarning;
};

inline const QRegularExpression &AutoMapper::mapNameFilter() const
{
return mMapNameFilter;
}

} // namespace Tiled
6 changes: 3 additions & 3 deletions src/tiled/automapperwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@
using namespace Tiled;

AutoMapperWrapper::AutoMapperWrapper(MapDocument *mapDocument,
const std::vector<std::unique_ptr<AutoMapper>> &autoMappers,
const QVector<AutoMapper *> &autoMappers,
const QRegion &where,
const TileLayer *touchedLayer)
: mMapDocument(mapDocument)
{
AutoMappingContext context(mapDocument);

for (const auto &autoMapper : autoMappers)
for (const auto autoMapper : autoMappers)
autoMapper->prepareAutoMap(context);

// Store a copy of each output tile layer before AutoMapping.
Expand Down Expand Up @@ -69,7 +69,7 @@ AutoMapperWrapper::AutoMapperWrapper(MapDocument *mapDocument,
const Map *map = mapDocument->map();
const QRegion mapRect(0, 0, map->width(), map->height());

for (const auto &autoMapper : autoMappers) {
for (const auto autoMapper : autoMappers) {
// stop expanding region when it's already the entire fixed-size map
if (appliedRegionPtr && (!map->infinite() && (mapRect - region).isEmpty()))
appliedRegionPtr = nullptr;
Expand Down
2 changes: 1 addition & 1 deletion src/tiled/automapperwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AutoMapperWrapper : public QUndoCommand
{
public:
AutoMapperWrapper(MapDocument *mapDocument,
const std::vector<std::unique_ptr<AutoMapper>> &autoMappers,
const QVector<AutoMapper*> &autoMappers,
const QRegion &where,
const TileLayer *touchedLayer = nullptr);
~AutoMapperWrapper() override;
Expand Down
77 changes: 55 additions & 22 deletions src/tiled/automappingmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <QDir>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QScopeGuard>
#include <QScopedValueRollback>
#include <QTextStream>

using namespace Tiled;
Expand All @@ -41,6 +43,8 @@ SessionOption<bool> AutomappingManager::automappingWhileDrawing { "automapping.w
AutomappingManager::AutomappingManager(QObject *parent)
: QObject(parent)
{
mMapNameFilter.setPatternOptions(QRegularExpression::CaseInsensitiveOption);

connect(&mWatcher, &QFileSystemWatcher::fileChanged,
this, &AutomappingManager::onFileChanged);
}
Expand Down Expand Up @@ -119,31 +123,44 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
}
}

// Even if no AutoMapper instance will be executed, we still want to report
// any warnings or errors that might have been reported while interpreting
// the rule maps.
auto reportErrors = qScopeGuard([=] {
if (!mWarning.isEmpty())
emit warningsOccurred(automatic);

if (!mError.isEmpty())
emit errorsOccurred(automatic);
});

// Determine the list of AutoMappers that is relevant for this map
const QString mapFileName = QFileInfo(mMapDocument->fileName()).fileName();
QVector<AutoMapper*> autoMappers;
autoMappers.reserve(mAutoMappers.size());
for (const auto &autoMapper : mAutoMappers) {
const auto &mapNameFilter = autoMapper->mapNameFilter();
if (!mapNameFilter.isValid() || mapNameFilter.match(mapFileName).hasMatch())
autoMappers.append(autoMapper.get());
}

if (autoMappers.isEmpty())
return;

// Skip this AutoMapping run if none of the loaded rule maps actually use
// the touched layer.
if (touchedLayer) {
if (std::none_of(mAutoMappers.cbegin(),
mAutoMappers.cend(),
[=] (const std::unique_ptr<AutoMapper> &autoMapper) { return autoMapper->ruleLayerNameUsed(touchedLayer->name()); }))
if (std::none_of(autoMappers.cbegin(),
autoMappers.cend(),
[=] (AutoMapper *autoMapper) { return autoMapper->ruleLayerNameUsed(touchedLayer->name()); }))
return;
}

QUndoStack *undoStack = mMapDocument->undoStack();
undoStack->beginMacro(tr("Apply AutoMap rules"));
AutoMapperWrapper *aw = new AutoMapperWrapper(mMapDocument, mAutoMappers, where, touchedLayer);
AutoMapperWrapper *aw = new AutoMapperWrapper(mMapDocument, autoMappers, where, touchedLayer);
undoStack->push(aw);
undoStack->endMacro();

for (auto &autoMapper : qAsConst(mAutoMappers)) {
mWarning += autoMapper->warningString();
mError += autoMapper->errorString();
}

if (!mWarning.isEmpty())
emit warningsOccurred(automatic);

if (!mError.isEmpty())
emit errorsOccurred(automatic);
}

/**
Expand All @@ -157,8 +174,13 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
*/
bool AutomappingManager::loadFile(const QString &filePath)
{
if (filePath.endsWith(QLatin1String(".txt"), Qt::CaseInsensitive))
if (filePath.endsWith(QLatin1String(".txt"), Qt::CaseInsensitive)) {
// Restore any potential change to the map name filter after processing
// the included rules file.
QScopedValueRollback<QRegularExpression> mapNameFilter(mMapNameFilter);

return loadRulesFile(filePath);
}

return loadRuleMap(filePath);
}
Expand Down Expand Up @@ -191,13 +213,24 @@ bool AutomappingManager::loadRulesFile(const QString &filePath)
QTextStream in(&rulesFile);

for (QString line = in.readLine(); !line.isNull(); line = in.readLine()) {
QString rulePath = line.trimmed();
if (rulePath.isEmpty()
|| rulePath.startsWith(QLatin1Char('#'))
|| rulePath.startsWith(QLatin1String("//")))
auto trimmedLine = QStringView(line).trimmed();
if (trimmedLine.isEmpty()
|| trimmedLine.startsWith(QLatin1Char('#'))
|| trimmedLine.startsWith(QLatin1String("//")))
continue;

if (trimmedLine.startsWith(QLatin1Char('[')) && trimmedLine.endsWith(QLatin1Char(']'))) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
auto filter = trimmedLine.mid(1, trimmedLine.length() - 2);
mMapNameFilter.setPattern(QRegularExpression::wildcardToRegularExpression(filter.toString()));
#else
auto filter = trimmedLine.sliced(1, trimmedLine.length() - 2);
mMapNameFilter.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
#endif
continue;
}

rulePath = absPath.filePath(rulePath);
const QString rulePath = absPath.filePath(trimmedLine.toString());

if (!QFileInfo::exists(rulePath)) {
QString error = tr("File not found: '%1' (referenced by '%2')")
Expand Down Expand Up @@ -232,7 +265,7 @@ bool AutomappingManager::loadRuleMap(const QString &filePath)
return false;
}

std::unique_ptr<AutoMapper> autoMapper { new AutoMapper(std::move(rules)) };
std::unique_ptr<AutoMapper> autoMapper { new AutoMapper(std::move(rules), mMapNameFilter) };

mWarning += autoMapper->warningString();
const QString error = autoMapper->errorString();
Expand Down
4 changes: 3 additions & 1 deletion src/tiled/automappingmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

#include "session.h"

#include <QFileSystemWatcher>
#include <QObject>
#include <QRegion>
#include <QRegularExpression>
#include <QString>
#include <QFileSystemWatcher>

#include <memory>
#include <vector>
Expand Down Expand Up @@ -131,6 +132,7 @@ class AutomappingManager : public QObject
QFileSystemWatcher mWatcher;

QString mRulesFile;
QRegularExpression mMapNameFilter;
bool mRulesFileOverride = false;
};

Expand Down

0 comments on commit cd635c5

Please sign in to comment.