Skip to content

Commit

Permalink
Give scripts access to existing file formats (#2716)
Browse files Browse the repository at this point in the history
Scripts can now call pre-defined map/tileset formats, and can query what
map/tileset formats are available.

Closes #2696
  • Loading branch information
Phlosioneer authored and bjorn committed Jan 7, 2020
1 parent fef03ba commit 157605b
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 0 deletions.
95 changes: 95 additions & 0 deletions docs/reference/scripting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ Properties
**openAssets** : array |ro|, "List of currently opened :ref:`assets <script-asset>`."
**mapEditor** : :ref:`script-mapeditor`, "Access the editor used when editing maps."
**tilesetEditor** : :ref:`script-tileseteditor`, "Access the editor used when editing tilesets."
**tilesetFormats** : [string] |ro|, "List of supported tileset format names. Use
:ref:`tilesetFormat <script-tilesetFormat>` to get the corresponding format object
to read and write files. (Since 1.4)"
**mapFormats** : [string] |ro|, "List of supported map format names. Use
:ref:`mapFormat <script-mapFormat>` to get the corresponding format object to
read and write files. (Since 1.4)"

Functions
~~~~~~~~~
Expand Down Expand Up @@ -432,6 +438,31 @@ tiled.extendMenu(id : string, items : array | object) : void
The "CustomAction" will need to have been registered before using
:ref:`tiled.registerAction() <script-registerAction>`.


.. _script-tilesetFormat:

tiled.tilesetFormat(shortName : string) : :ref:`script-tilesetformatwrapper`
Returns the tileset format object with the given name, or `undefined` if
no object was found. See the `tilesetFormats` property for more info.

.. _script-tilesetFormatForFile:

tiled.tilesetFormatForFile(fileName : string) : :ref:`script-tilesetformatwrapper`
Returns the tileset format object that can read the given file, or `undefined`
if no object was found.

.. _script-mapFormat:

tiled.mapFormat(shortName : string) : :ref:`script-mapformatwrapper`
Returns the map format object with the given name, or `undefined` if no object
was found. See the `mapFormats` property for more info.

.. _script-mapFormatForFile:

tiled.mapFormatForFile(fileName : string) : :ref:`script-mapformatwrapper`
Returns the map format object that can read the given file, or `undefined` if
no object was found.

.. _script-tiled-signals:

Signals
Expand Down Expand Up @@ -537,6 +568,28 @@ Asset.macro(text : string, callback : function) : value
The returned value is whatever the callback function returned.

.. _script-fileformat:

File Format
^^^^^^^^^^^

Common functionality for file format readers and writers. (Since 1.4)

Properties
~~~~~~~~~~

.. csv-table::
widths: 1, 2

**canRead** : bool |ro|, Whether this format supports reading files.
**canWrite** : bool |ro|, Whether this format supports writing files.

Functions
~~~~~~~~~

FileFormat.supportsFile(fileName : string) : bool
Returns whether the file is readable by this format.

.. _script-grouplayer:

GroupLayer
Expand Down Expand Up @@ -690,6 +743,27 @@ Properties
**currentMapView** : :ref:`script-mapview` |ro|, "Access the current map view."
**tilesetsView** : :ref:`script-tilesetsview` |ro|, "Access the Tilesets view."

.. _script-mapformatwrapper:

Map Format
^^^^^^^^^^

This is an object that can read or write map files. (Since 1.4)

Inherits :ref:`script-fileformat`.

Functions
~~~~~~~~~

MapFormat.read(fileName : string) : :ref:`script-map`
Read the given file as a map. This function will throw an error if it
is not supported.

MapFormat.write(map : :ref:`script-map`, fileName : string) : string
Write the given map to a file. This function will throw an error if it
is not supported. If there is an error writing the file, it will return a
description of the error; otherwise, it will return "".

.. _script-mapview:

Map View
Expand Down Expand Up @@ -1296,6 +1370,27 @@ Properties

**collisionEditor** : :ref:`script-tilecollisioneditor`, "Access the collision editor within the tileset editor."

.. _script-tilesetformatwrapper:

Tileset Format
^^^^^^^^^^^^^^

This is an object that can read or write tileset files. (Since 1.4)

Inherits :ref:`script-fileformat`.

Functions
~~~~~~~~~

TilesetFormat.read(fileName : string) : :ref:`script-tileset`
Read the given file as a tileset. This function will throw an error if it
is not supported.

TilesetFormat.write(tileset : :ref:`script-tileset`, fileName : string) : string
Write the given tileset to a file. This function will throw an error if it
is not supported. If there is an error writing the file, it will return a
description of the error; otherwise, it will return "".

.. _script-tilesetsview:

Tilesets View
Expand Down
135 changes: 135 additions & 0 deletions src/tiled/scriptfileformatwrappers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* scriptfileformatwrappers.h
* Copyright 2019, Phlosioneer <mattmdrr2@gmail.com>
*
* 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 "scriptfileformatwrappers.h"

#include "editablemap.h"
#include "editabletileset.h"
#include "mapformat.h"
#include "scriptedfileformat.h"
#include "scriptmanager.h"
#include "tilesetformat.h"

#include <QCoreApplication>
#include <QQmlEngine>

namespace Tiled {

ScriptFileFormatWrapper::ScriptFileFormatWrapper(FileFormat *format, QObject *parent)
: QObject(parent),
mFormat(format)
{}

bool ScriptFileFormatWrapper::supportsFile(const QString &filename) const
{
return mFormat->supportsFile(filename);
}

bool ScriptFileFormatWrapper::canRead() const
{
return mFormat->capabilities() & FileFormat::Read;
}

bool ScriptFileFormatWrapper::canWrite() const
{
return mFormat->capabilities() & FileFormat::Write;
}

bool ScriptFileFormatWrapper::assertCanRead() const
{
if (canRead())
return true;
auto message = QCoreApplication::translate("Script Errors", "File format doesn't support `read`");
ScriptManager::instance().throwError(message);
return false;
}

bool ScriptFileFormatWrapper::assertCanWrite() const
{
if (canWrite())
return true;
auto message = QCoreApplication::translate("Script Errors", "File format doesn't support `write`");
ScriptManager::instance().throwError(message);
return false;
}


ScriptTilesetFormatWrapper::ScriptTilesetFormatWrapper(TilesetFormat* format, QObject *parent)
: ScriptFileFormatWrapper(format, parent)
{}

EditableTileset *ScriptTilesetFormatWrapper::read(const QString &filename)
{
if (!assertCanRead())
return nullptr;

auto tileset = static_cast<TilesetFormat*>(mFormat)->read(filename);
if (!tileset) {
auto message = QCoreApplication::translate("Script Errors", "Error reading tileset");
ScriptManager::instance().throwError(message);
return nullptr;
}

return new EditableTileset(tileset.data());
}

void ScriptTilesetFormatWrapper::write(EditableTileset *editable, const QString &filename)
{
if (!assertCanWrite())
return;

auto tileset = editable->tileset();
auto success = static_cast<TilesetFormat*>(mFormat)->write(*tileset, filename);
if (!success)
ScriptManager::instance().throwError(mFormat->errorString());
}


ScriptMapFormatWrapper::ScriptMapFormatWrapper(MapFormat *format, QObject *parent)
: ScriptFileFormatWrapper(format, parent)
{}

EditableMap *ScriptMapFormatWrapper::read(const QString &filename)
{
if (!assertCanRead())
return nullptr;

auto map = static_cast<MapFormat*>(mFormat)->read(filename);
if (!map) {
auto message = QCoreApplication::translate("Script Errors", "Error reading map");
ScriptManager::instance().throwError(message);
return nullptr;
}

return new EditableMap(std::move(map));
}

void ScriptMapFormatWrapper::write(EditableMap *editable, const QString &filename)
{
if (!assertCanWrite())
return;

auto map = editable->map();
auto success = static_cast<MapFormat*>(mFormat)->write(map, filename);
if (!success)
ScriptManager::instance().throwError(mFormat->errorString());
}

} // namespace Tiled
80 changes: 80 additions & 0 deletions src/tiled/scriptfileformatwrappers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* scriptfileformatwrappers.h
* Copyright 2019, Phlosioneer <mattmdrr2@gmail.com>
*
* 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/>.
*/

#pragma once

#include <QObject>

namespace Tiled {

class EditableTileset;
class EditableMap;
class MapFormat;
class TilesetFormat;
class FileFormat;

class ScriptFileFormatWrapper : public QObject
{
Q_OBJECT

Q_PROPERTY(bool canRead READ canRead)
Q_PROPERTY(bool canWrite READ canWrite)

public:
explicit ScriptFileFormatWrapper(FileFormat *format, QObject *parent = nullptr);

Q_INVOKABLE bool supportsFile(const QString &filename) const;

bool canRead() const;
bool canWrite() const;

protected:
bool assertCanRead() const;
bool assertCanWrite() const;

FileFormat *mFormat;
};

class ScriptTilesetFormatWrapper : public ScriptFileFormatWrapper
{
Q_OBJECT

public:
explicit ScriptTilesetFormatWrapper(TilesetFormat *format, QObject *parent = nullptr);

Q_INVOKABLE Tiled::EditableTileset *read(const QString &filename);
Q_INVOKABLE void write(Tiled::EditableTileset *tileset, const QString &filename);
};

class ScriptMapFormatWrapper : public ScriptFileFormatWrapper
{
Q_OBJECT

public:
explicit ScriptMapFormatWrapper(MapFormat *format, QObject *parent = nullptr);

Q_INVOKABLE Tiled::EditableMap *read(const QString &filename);
Q_INVOKABLE void write(Tiled::EditableMap *map, const QString &filename);
};

} // namespace Tiled

Q_DECLARE_METATYPE(Tiled::ScriptTilesetFormatWrapper*)
Q_DECLARE_METATYPE(Tiled::ScriptMapFormatWrapper*)
3 changes: 3 additions & 0 deletions src/tiled/scriptmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "scriptedfileformat.h"
#include "scriptedtool.h"
#include "scriptfile.h"
#include "scriptfileformatwrappers.h"
#include "scriptmodule.h"
#include "tilecollisiondock.h"
#include "tilelayer.h"
Expand Down Expand Up @@ -104,6 +105,8 @@ ScriptManager::ScriptManager(QObject *parent)
qRegisterMetaType<TileLayerEdit*>();
qRegisterMetaType<TilesetDock*>();
qRegisterMetaType<TilesetEditor*>();
qRegisterMetaType<ScriptMapFormatWrapper*>();
qRegisterMetaType<ScriptTilesetFormatWrapper*>();

connect(&mWatcher, &FileSystemWatcher::filesChanged,
this, &ScriptManager::scriptFilesChanged);
Expand Down
Loading

0 comments on commit 157605b

Please sign in to comment.