From 185011f469e05f58983bd6e73b6df3d4abdf9432 Mon Sep 17 00:00:00 2001 From: Jocelyn <15899212+5gTestSubject@users.noreply.github.com> Date: Mon, 1 Dec 2025 07:06:22 -0500 Subject: [PATCH] Added collision capsule support (#4288) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to ellipse, but with different rendering. Closes #2153 Co-authored-by: Thorbjørn Lindeijer --- NEWS.md | 1 + docs/manual/objects.rst | 44 ++++++++++--- docs/reference/json-map-format.rst | 11 ++-- docs/reference/tmx-changelog.rst | 10 +++ docs/reference/tmx-map-format.rst | 15 ++++- src/libtiled/isometricrenderer.cpp | 21 +++++-- src/libtiled/mapobject.cpp | 5 +- src/libtiled/mapobject.h | 1 + src/libtiled/mapreader.cpp | 4 ++ src/libtiled/maptovariantconverter.cpp | 4 ++ src/libtiled/mapwriter.cpp | 4 ++ src/libtiled/orthogonalrenderer.cpp | 30 ++++++++- src/libtiled/varianttomapconverter.cpp | 5 ++ src/plugins/lua/luaplugin.cpp | 3 + src/plugins/python/pythonbind.cpp | 4 ++ src/tiled/createcapsuleobjecttool.cpp | 58 ++++++++++++++++++ src/tiled/createcapsuleobjecttool.h | 43 +++++++++++++ src/tiled/editablemapobject.h | 1 + src/tiled/libtilededitor.qbs | 2 + src/tiled/mapeditor.cpp | 3 + src/tiled/mapobjectmodel.cpp | 3 + src/tiled/mapobjectmodel.h | 1 + src/tiled/objectselectiontool.cpp | 3 + .../resources/images/24/insert-capsule.png | Bin 0 -> 861 bytes .../resources/images/24/object-capsule.png | Bin 0 -> 642 bytes .../resources/images/48/insert-capsule.png | Bin 0 -> 1670 bytes src/tiled/resources/images/parts/icons.tmx | 21 ++++--- src/tiled/tilecollisiondock.cpp | 3 + 28 files changed, 269 insertions(+), 31 deletions(-) create mode 100644 src/tiled/createcapsuleobjecttool.cpp create mode 100644 src/tiled/createcapsuleobjecttool.h create mode 100644 src/tiled/resources/images/24/insert-capsule.png create mode 100644 src/tiled/resources/images/24/object-capsule.png create mode 100644 src/tiled/resources/images/48/insert-capsule.png diff --git a/NEWS.md b/NEWS.md index edb06b88b1..4fe0646254 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ * Rewritten Properties view to enable direct widget interaction (#4045) * Added support for lists in custom properties (#1493) +* Added capsule object shape (by Jocelyn, #2153) * Allow filtering tilesets by name in the tileset dock (with dogboydog, #4239) * Allow changing the values of number inputs using expressions (with dogboydog, #4234) * Added support for SVG 1.2 / CSS blending modes to layers (#3932) diff --git a/docs/manual/objects.rst b/docs/manual/objects.rst index e9617a2576..3a391bf436 100644 --- a/docs/manual/objects.rst +++ b/docs/manual/objects.rst @@ -37,7 +37,7 @@ to switch to the :ref:`select-objects-tool` tool. Insert Rectangle ~~~~~~~~~~~~~~~~ -Shortcut: ``R`` +Shortcut: ``R`` |insert-rectangle| The rectangle was the first type of object supported by Tiled, which is why objects are rectangles by default in the :doc:`/reference/tmx-map-format`. They @@ -61,7 +61,7 @@ around its position. This is mainly to keep it visible and selectable. Insert Point ~~~~~~~~~~~~~~ -Shortcut: ``I`` +Shortcut: ``I`` |insert-point| Points are the simplest objects you can place on a map. They only represent a location, and cannot be resized or rotated. Simply click on the map to position @@ -72,18 +72,35 @@ a point object. Insert Ellipse ~~~~~~~~~~~~~~ -Shortcut: ``C`` +Shortcut: ``C`` |insert-ellipse| Ellipses work the same way as `rectangles <#insert-rectangle>`__, except that they are rendered as an ellipse. Useful for when your area or collision shape needs to represent a circle or ellipse. + +.. raw:: html + +
Since Tiled 1.12
+ + +.. _insert-capsule-tool: + +Insert Capsule +~~~~~~~~~~~~~~ + +Shortcut: ``Shift+C`` |insert-capsule| + +Capsules work the same way as `rectangles <#insert-rectangle>`__, except +that they are rendered as a capsule. Useful for when your area or +collision shape needs to represent a circle or capsule. + .. _insert-polygon-tool: Insert Polygon ~~~~~~~~~~~~~~ -Shortcut: ``P`` +Shortcut: ``P`` |insert-polygon| Polygons are the most flexible way of defining the shape of an area. They are most commonly used for defining collision shapes. @@ -125,7 +142,7 @@ The :ref:`edit-polygons-tool` tool is used to edit polylines as well. Insert Tile ~~~~~~~~~~~ -Shortcut: ``T`` +Shortcut: ``T`` |insert-tile| Tiles can be inserted as objects to have full flexibility in placing, scaling and rotating the tile image on your map. Like all objects, tile @@ -168,7 +185,7 @@ objects consistent with that of :ref:`rectangle objects Insert Template ~~~~~~~~~~~~~~~ -Shortcut: ``V`` +Shortcut: ``V`` |insert-template| Can be used to quickly insert multiple instances of the template selected in the Templates view. See :ref:`creating-template-instances`. @@ -178,7 +195,7 @@ selected in the Templates view. See :ref:`creating-template-instances`. Insert Text ~~~~~~~~~~~ -Shortcut: ``X`` +Shortcut: ``X`` |insert-text| Text objects can be used to add arbitrary multi-line text to your maps. You can configure various font properties and the wrapping / clipping @@ -190,7 +207,7 @@ the game. Select Objects -------------- -Shortcut: ``S`` +Shortcut: ``S`` |tool-select-objects| When you're not inserting new objects, you're generally using the Select Objects tool. It packs a lot of functionality, which is outlined below. @@ -384,3 +401,14 @@ jump there by right-clicking the property and selecting *Go to Object*. faster by `sponsoring Tiled development `__. The more support I receive the more time I can afford to spend improving Tiled! + + +.. |tool-select-objects| image:: ../../src/tiled/resources/images/22/tool-select-objects.png +.. |insert-rectangle| image:: ../../src/tiled/resources/images/24/insert-rectangle.png +.. |insert-point| image:: ../../src/tiled/resources/images/24/insert-point.png +.. |insert-ellipse| image:: ../../src/tiled/resources/images/24/insert-ellipse.png +.. |insert-capsule| image:: ../../src/tiled/resources/images/24/insert-capsule.png +.. |insert-polygon| image:: ../../src/tiled/resources/images/24/insert-polygon.png +.. |insert-tile| image:: ../../src/tiled/resources/images/24/insert-image.png +.. |insert-template| image:: ../../src/tiled/resources/images/24/insert-template.png +.. |insert-text| image:: ../../src/tiled/resources/images/24/insert-text.png diff --git a/docs/reference/json-map-format.rst b/docs/reference/json-map-format.rst index f7907cc57a..3b316854f1 100644 --- a/docs/reference/json-map-format.rst +++ b/docs/reference/json-map-format.rst @@ -233,6 +233,7 @@ Object :header: Field, Type, Description :widths: 1, 1, 4 + capsule, bool, "Used to mark an object as a capsule" ellipse, bool, "Used to mark an object as an ellipse" gid, int, "Global tile ID, only if object represents a tile" height, double, "Height in pixels." @@ -492,10 +493,10 @@ Tileset wangsets, array, "Array of :ref:`Wang sets ` (since 1.1.5)" Each tileset has a ``firstgid`` (first global ID) property which -tells you the global ID of its first tile (the one with local -tile ID 0). This allows you to map the global IDs back to the -right tileset, and then calculate the local tile ID by -subtracting the ``firstgid`` from the global tile ID. The first +tells you the global ID of its first tile (the one with local +tile ID 0). This allows you to map the global IDs back to the +right tileset, and then calculate the local tile ID by +subtracting the ``firstgid`` from the global tile ID. The first tileset always has a ``firstgid`` value of 1. .. _json-tileset-grid: @@ -769,6 +770,8 @@ Tiled 1.12 * Added ``mode`` property to :ref:`json-layer` to specify the blend mode to use when rendering the layer. +* Added ``capsule`` property to :ref:`json-object`. + Tiled 1.11.1 ~~~~~~~~~~~~ diff --git a/docs/reference/tmx-changelog.rst b/docs/reference/tmx-changelog.rst index 066eb11309..3855063b1a 100644 --- a/docs/reference/tmx-changelog.rst +++ b/docs/reference/tmx-changelog.rst @@ -9,6 +9,16 @@ Tiled 1.12 - Added ``mode`` attribute on :ref:`tmx-layer` to specific its blend mode. +- Added capsule object shape. Same parameters as rectangular objects, + but marked as capsule with a child element: + + .. code:: xml + + + + + + Tiled 1.10 ---------- diff --git a/docs/reference/tmx-map-format.rst b/docs/reference/tmx-map-format.rst index 39b3586ce5..df50932cd0 100644 --- a/docs/reference/tmx-map-format.rst +++ b/docs/reference/tmx-map-format.rst @@ -578,9 +578,9 @@ properties from the specified template, properties saved with the object will have higher priority, i.e. they will override the template properties. -Can contain at most one: :ref:`tmx-properties`, :ref:`tmx-ellipse` (since -0.9), :ref:`tmx-point` (since 1.1), :ref:`tmx-polygon`, :ref:`tmx-polyline`, -:ref:`tmx-text` (since 1.0) +Can contain at most one: :ref:`tmx-properties`, :ref:`tmx-ellipse` (since 0.9), +:ref:`tmx-capsule` (since 1.12), :ref:`tmx-point` (since 1.1), +:ref:`tmx-polygon`, :ref:`tmx-polyline`, :ref:`tmx-text` (since 1.0) .. _tmx-ellipse: @@ -591,6 +591,15 @@ Used to mark an object as an ellipse. The existing ``x``, ``y``, ``width`` and ``height`` attributes are used to determine the size of the ellipse. +.. _tmx-capsule: + + +~~~~~~~~~ + +Used to mark an object as a capsule. The existing ``x``, ``y``, +``width`` and ``height`` attributes are used to determine the size of +the capsule. + .. _tmx-point: diff --git a/src/libtiled/isometricrenderer.cpp b/src/libtiled/isometricrenderer.cpp index a5b6587341..3f85f3feec 100644 --- a/src/libtiled/isometricrenderer.cpp +++ b/src/libtiled/isometricrenderer.cpp @@ -120,6 +120,15 @@ QPainterPath IsometricRenderer::shape(const MapObject *object) const QPainterPath path; switch (object->shape()) { + case MapObject::Capsule: { + QRectF bounds = object->bounds(); + bounds.translate(-alignmentOffset(bounds, object->alignment(map()))); + const qreal rad = std::min(std::abs(bounds.height()), + std::abs(bounds.width())) / 2; + path.addRoundedRect(bounds, rad, rad, Qt::SizeMode::AbsoluteSize); + path = transform().map(path); + break; + } case MapObject::Ellipse: { QRectF bounds = object->bounds(); bounds.translate(-alignmentOffset(bounds, object->alignment(map()))); @@ -166,7 +175,8 @@ QPainterPath IsometricRenderer::interactionShape(const MapObject *object) const } else { switch (object->shape()) { case MapObject::Rectangle: - case MapObject::Ellipse: { + case MapObject::Ellipse: + case MapObject::Capsule: { QRectF bounds = object->bounds(); bounds.translate(-alignmentOffset(bounds, object->alignment(map()))); @@ -404,18 +414,19 @@ void IsometricRenderer::drawMapObject(QPainter *painter, // TODO: Do something sensible to make null-sized objects usable switch (object->shape()) { - case MapObject::Ellipse: { + case MapObject::Ellipse: + case MapObject::Capsule: { const QPolygonF rect = pixelRectToScreenPolygon(bounds); - const QPainterPath ellipse = shape(object); + const QPainterPath path = shape(object); - painter->drawPath(ellipse.translated(shadowOffset)); + painter->drawPath(path.translated(shadowOffset)); painter->drawPolygon(rect.translated(shadowOffset)); painter->setPen(colorPen); painter->drawPolygon(rect); painter->setBrush(brush); - painter->drawPath(ellipse); + painter->drawPath(path); break; } case MapObject::Point: diff --git a/src/libtiled/mapobject.cpp b/src/libtiled/mapobject.cpp index a517801a98..be157ed768 100644 --- a/src/libtiled/mapobject.cpp +++ b/src/libtiled/mapobject.cpp @@ -178,6 +178,7 @@ QRectF MapObject::screenBounds(const MapRenderer &renderer) const } else { switch (mShape) { case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Rectangle: { QRectF bounds(this->bounds()); align(bounds, alignment(renderer.map())); @@ -491,8 +492,8 @@ void MapObject::flipInScreenCoordinates(FlipDirection direction, const QPointF & } /** - * Rectangles, ellipses and polygons are flipped in "pixel space", the - * coordinate space before isometric projection. + * Rectangles, ellipses, capsules and polygons are flipped in "pixel space", + * the coordinate space before isometric projection. * * NOTE: No attempt to handle rotated shapes on isometric maps is made. The * expectation is that due to their weird handling, nobody really does that diff --git a/src/libtiled/mapobject.h b/src/libtiled/mapobject.h index 4e8ae8ab27..77f7b3e0d6 100644 --- a/src/libtiled/mapobject.h +++ b/src/libtiled/mapobject.h @@ -99,6 +99,7 @@ class TILEDSHARED_EXPORT MapObject : public Object Polygon, Polyline, Ellipse, + Capsule, Text, Point, }; diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index 5c6e646e42..ee58d5708a 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -1252,6 +1252,10 @@ std::unique_ptr MapReaderPrivate::readObject() xml.skipCurrentElement(); object->setShape(MapObject::Ellipse); object->setPropertyChanged(MapObject::ShapeProperty); + } else if (xml.name() == QLatin1String("capsule")) { + xml.skipCurrentElement(); + object->setShape(MapObject::Capsule); + object->setPropertyChanged(MapObject::ShapeProperty); } else if (xml.name() == QLatin1String("text")) { object->setTextData(readObjectText()); object->setShape(MapObject::Text); diff --git a/src/libtiled/maptovariantconverter.cpp b/src/libtiled/maptovariantconverter.cpp index ac1dc93ff6..603a6639a2 100644 --- a/src/libtiled/maptovariantconverter.cpp +++ b/src/libtiled/maptovariantconverter.cpp @@ -622,6 +622,10 @@ QVariant MapToVariantConverter::toVariant(const MapObject &object) const if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty)) objectVariant[QStringLiteral("ellipse")] = true; break; + case MapObject::Capsule: + if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty)) + objectVariant[QStringLiteral("capsule")] = true; + break; case MapObject::Text: if (notTemplateInstance || (object.propertyChanged(MapObject::TextProperty) || object.propertyChanged(MapObject::TextFontProperty) || diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index fc41107538..d90e00f85c 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -793,6 +793,10 @@ void MapWriterPrivate::writeObject(QXmlStreamWriter &w, if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::ShapeProperty))) w.writeEmptyElement(QLatin1String("ellipse")); break; + case MapObject::Capsule: + if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::ShapeProperty))) + w.writeEmptyElement(QLatin1String("capsule")); + break; case MapObject::Text: { if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::TextProperty) || diff --git a/src/libtiled/orthogonalrenderer.cpp b/src/libtiled/orthogonalrenderer.cpp index e8096d1bcd..e5fd9360a1 100644 --- a/src/libtiled/orthogonalrenderer.cpp +++ b/src/libtiled/orthogonalrenderer.cpp @@ -77,6 +77,7 @@ QRectF OrthogonalRenderer::boundingRect(const MapObject *object) const switch (object->shape()) { case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Rectangle: if (bounds.isNull()) { boundingRect = bounds.adjusted(-10 - extraSpace, @@ -172,6 +173,15 @@ QPainterPath OrthogonalRenderer::shape(const MapObject *object) const path.addEllipse(bounds); break; } + case MapObject::Capsule: { + const qreal rad = std::min(std::abs(bounds.height()), + std::abs(bounds.width())) / 2; + if (rad == 0.) + path.addEllipse(bounds.topLeft(), 10, 10); + else + path.addRoundedRect(bounds, rad, rad, Qt::SizeMode::AbsoluteSize); + break; + } case MapObject::Point: path = pointShape(object->position()); break; @@ -203,6 +213,7 @@ QPainterPath OrthogonalRenderer::interactionShape(const MapObject *object) const case MapObject::Rectangle: case MapObject::Polygon: case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Text: path = shape(object); break; @@ -394,7 +405,7 @@ void OrthogonalRenderer::drawMapObject(QPainter *painter, // CoreGraphics when drawing the path requested by the // QCoreGraphicsPaintEngine. Draw them as rectangle instead. MapObject::Shape shape = object->shape(); - if (shape == MapObject::Ellipse && + if ((shape == MapObject::Ellipse || shape == MapObject::Capsule) && ((bounds.width() == qreal(0)) ^ (bounds.height() == qreal(0)))) { shape = MapObject::Rectangle; } @@ -458,6 +469,23 @@ void OrthogonalRenderer::drawMapObject(QPainter *painter, break; } + case MapObject::Capsule: { + if (bounds.isNull()) + bounds = QRectF(QPointF(-10, -10), QSizeF(20, 20)); + + const qreal rad = std::min(std::abs(bounds.height()), + std::abs(bounds.width())) / 2; + + // Draw the shadow + painter->setPen(shadowPen); + painter->drawRoundedRect(bounds.translated(shadowOffset), rad, rad); + + painter->setPen(linePen); + painter->setBrush(fillBrush); + painter->drawRoundedRect(bounds, rad, rad); + break; + } + case MapObject::Text: { const auto& textData = object->textData(); painter->setFont(textData.font); diff --git a/src/libtiled/varianttomapconverter.cpp b/src/libtiled/varianttomapconverter.cpp index 0cff37447a..4e83959751 100644 --- a/src/libtiled/varianttomapconverter.cpp +++ b/src/libtiled/varianttomapconverter.cpp @@ -806,6 +806,7 @@ std::unique_ptr VariantToMapConverter::toMapObject(const QVariantMap const QVariant polylineVariant = variantMap[QStringLiteral("polyline")]; const QVariant polygonVariant = variantMap[QStringLiteral("polygon")]; const QVariant ellipseVariant = variantMap[QStringLiteral("ellipse")]; + const QVariant capsuleVariant = variantMap[QStringLiteral("capsule")]; const QVariant pointVariant = variantMap[QStringLiteral("point")]; const QVariant textVariant = variantMap[QStringLiteral("text")]; @@ -823,6 +824,10 @@ std::unique_ptr VariantToMapConverter::toMapObject(const QVariantMap object->setShape(MapObject::Ellipse); object->setPropertyChanged(MapObject::ShapeProperty); } + if (capsuleVariant.toBool()) { + object->setShape(MapObject::Capsule); + object->setPropertyChanged(MapObject::ShapeProperty); + } if (pointVariant.toBool()) { object->setShape(MapObject::Point); object->setPropertyChanged(MapObject::ShapeProperty); diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index 64db6cce8a..c088302b66 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -708,6 +708,8 @@ static const char *toString(MapObject::Shape shape) return "polyline"; case MapObject::Ellipse: return "ellipse"; + case MapObject::Capsule: + return "capsule"; case MapObject::Text: return "text"; case MapObject::Point: @@ -739,6 +741,7 @@ void LuaWriter::writeMapObject(const Tiled::MapObject *mapObject) switch (mapObject->shape()) { case MapObject::Rectangle: case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Point: break; case MapObject::Polygon: diff --git a/src/plugins/python/pythonbind.cpp b/src/plugins/python/pythonbind.cpp index 18d5f5cb3d..52313f23f0 100644 --- a/src/plugins/python/pythonbind.cpp +++ b/src/plugins/python/pythonbind.cpp @@ -7675,6 +7675,10 @@ inittiled_Tiled(void) // Tiled::MapObject::Ellipse tmp_value = PyLong_FromLong(Tiled::MapObject::Ellipse); PyDict_SetItemString((PyObject*) PyTiledMapObject_Type.tp_dict, "Ellipse", tmp_value); + Py_DECREF(tmp_value); + // Tiled::MapObject::Capsule + tmp_value = PyLong_FromLong(Tiled::MapObject::Capsule); + PyDict_SetItemString((PyObject*) PyTiledMapObject_Type.tp_dict, "Capsule", tmp_value); Py_DECREF(tmp_value); // Tiled::MapObject::Text tmp_value = PyLong_FromLong(Tiled::MapObject::Text); diff --git a/src/tiled/createcapsuleobjecttool.cpp b/src/tiled/createcapsuleobjecttool.cpp new file mode 100644 index 0000000000..da012dfe5d --- /dev/null +++ b/src/tiled/createcapsuleobjecttool.cpp @@ -0,0 +1,58 @@ +/* + * createcapsuleobjecttool.cpp + * Copyright 2025, Jocelyn + * + * 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 "createcapsuleobjecttool.h" + +#include "mapobject.h" +#include "utils.h" + +using namespace Tiled; + +CreateCapsuleObjectTool::CreateCapsuleObjectTool(QObject *parent) + : CreateScalableObjectTool("CreateCapsuleObjectTool", + parent) +{ + QIcon icon(QLatin1String(":images/24/insert-capsule.png")); + icon.addFile(QLatin1String(":images/48/insert-capsule.png")); + setIcon(icon); + setShortcut(Qt::SHIFT + Qt::Key_C); + Utils::setThemeIcon(this, "insert-capsule"); + languageChangedImpl(); +} + +void CreateCapsuleObjectTool::languageChanged() +{ + CreateScalableObjectTool::languageChanged(); + languageChangedImpl(); +} + +void CreateCapsuleObjectTool::languageChangedImpl() +{ + setName(tr("Insert Capsule")); +} + +MapObject *CreateCapsuleObjectTool::createNewMapObject() +{ + auto newMapObject = new MapObject; + newMapObject->setShape(MapObject::Capsule); + return newMapObject; +} + +#include "moc_createcapsuleobjecttool.cpp" diff --git a/src/tiled/createcapsuleobjecttool.h b/src/tiled/createcapsuleobjecttool.h new file mode 100644 index 0000000000..b71834f64d --- /dev/null +++ b/src/tiled/createcapsuleobjecttool.h @@ -0,0 +1,43 @@ +/* + * createcapsulebjecttool.h + * Copyright 2025, Jocelyn + * + * 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 "createscalableobjecttool.h" + +namespace Tiled { + +class CreateCapsuleObjectTool : public CreateScalableObjectTool +{ + Q_OBJECT + +public: + CreateCapsuleObjectTool(QObject *parent); + + void languageChanged() override; + +protected: + MapObject *createNewMapObject() override; + +private: + void languageChangedImpl(); +}; + +} // namespace Tiled diff --git a/src/tiled/editablemapobject.h b/src/tiled/editablemapobject.h index a43b971f1f..001f28b12a 100644 --- a/src/tiled/editablemapobject.h +++ b/src/tiled/editablemapobject.h @@ -86,6 +86,7 @@ class EditableMapObject : public EditableObject Polygon, Polyline, Ellipse, + Capsule, Text, Point, }; diff --git a/src/tiled/libtilededitor.qbs b/src/tiled/libtilededitor.qbs index 1733a758da..078531ca3b 100644 --- a/src/tiled/libtilededitor.qbs +++ b/src/tiled/libtilededitor.qbs @@ -178,6 +178,8 @@ DynamicLibrary { "commandsedit.ui", "consoledock.cpp", "consoledock.h", + "createcapsuleobjecttool.cpp", + "createcapsuleobjecttool.h", "createellipseobjecttool.cpp", "createellipseobjecttool.h", "createobjecttool.cpp", diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index c99388bace..80c529c6cd 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -23,6 +23,7 @@ #include "addremovetileset.h" #include "bucketfilltool.h" #include "changeselectedarea.h" +#include "createcapsuleobjecttool.h" #include "createellipseobjecttool.h" #include "createobjecttool.h" #include "createpointobjecttool.h" @@ -179,6 +180,7 @@ MapEditor::MapEditor(QObject *parent) CreateObjectTool *rectangleObjectsTool = new CreateRectangleObjectTool(this); CreateObjectTool *pointObjectsTool = new CreatePointObjectTool(this); CreateObjectTool *ellipseObjectsTool = new CreateEllipseObjectTool(this); + CreateObjectTool *capsuleObjectsTool = new CreateCapsuleObjectTool(this); CreateObjectTool *polygonObjectsTool = new CreatePolygonObjectTool(this); CreateObjectTool *textObjectsTool = new CreateTextObjectTool(this); @@ -197,6 +199,7 @@ MapEditor::MapEditor(QObject *parent) mToolsToolBar->addAction(mToolManager->registerTool(rectangleObjectsTool)); mToolsToolBar->addAction(mToolManager->registerTool(pointObjectsTool)); mToolsToolBar->addAction(mToolManager->registerTool(ellipseObjectsTool)); + mToolsToolBar->addAction(mToolManager->registerTool(capsuleObjectsTool)); mToolsToolBar->addAction(mToolManager->registerTool(polygonObjectsTool)); mToolsToolBar->addAction(mToolManager->registerTool(tileObjectsTool)); mToolsToolBar->addAction(mToolManager->registerTool(templatesTool)); diff --git a/src/tiled/mapobjectmodel.cpp b/src/tiled/mapobjectmodel.cpp index 26a644d403..4d06a54ba8 100644 --- a/src/tiled/mapobjectmodel.cpp +++ b/src/tiled/mapobjectmodel.cpp @@ -43,6 +43,7 @@ ObjectIconManager::ObjectIconManager() , mPolygonIcon(QLatin1String(":images/24/object-polygon.png")) , mPolylineIcon(QLatin1String(":images/24/object-polyline.png")) , mEllipseIcon(QLatin1String(":images/24/object-ellipse.png")) + , mCapsuleIcon(QLatin1String(":images/24/object-capsule.png")) , mTextIcon(QLatin1String(":images/24/object-text.png")) , mPointIcon(QLatin1String(":images/24/object-point.png")) {} @@ -64,6 +65,8 @@ const QIcon &ObjectIconManager::iconForObject(const MapObject &object) const return mPolylineIcon; case MapObject::Ellipse: return mEllipseIcon; + case MapObject::Capsule: + return mCapsuleIcon; case MapObject::Text: return mTextIcon; case MapObject::Point: diff --git a/src/tiled/mapobjectmodel.h b/src/tiled/mapobjectmodel.h index aebbc64e47..9abe7fa2c5 100644 --- a/src/tiled/mapobjectmodel.h +++ b/src/tiled/mapobjectmodel.h @@ -52,6 +52,7 @@ class ObjectIconManager const QIcon mPolygonIcon; const QIcon mPolylineIcon; const QIcon mEllipseIcon; + const QIcon mCapsuleIcon; const QIcon mTextIcon; const QIcon mPointIcon; }; diff --git a/src/tiled/objectselectiontool.cpp b/src/tiled/objectselectiontool.cpp index 2c6dd9187e..e5d4d7c5e6 100644 --- a/src/tiled/objectselectiontool.cpp +++ b/src/tiled/objectselectiontool.cpp @@ -828,6 +828,7 @@ static QRectF pixelBounds(const MapObject *object) switch (object->shape()) { case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Rectangle: case MapObject::Point: { QRectF bounds(object->bounds()); @@ -864,6 +865,7 @@ static bool canResizeAbsolute(const MapObject *object) switch (object->shape()) { case MapObject::Rectangle: case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Text: return true; case MapObject::Point: @@ -914,6 +916,7 @@ static QRectF objectBounds(const MapObject *object, } else { switch (object->shape()) { case MapObject::Ellipse: + case MapObject::Capsule: case MapObject::Rectangle: { QRectF bounds(object->bounds()); align(bounds, object->alignment()); diff --git a/src/tiled/resources/images/24/insert-capsule.png b/src/tiled/resources/images/24/insert-capsule.png new file mode 100644 index 0000000000000000000000000000000000000000..c58b0579524b7cc8b31d3f1d5b7e5e4696385378 GIT binary patch literal 861 zcmV-j1ETziP)!l9g09QpYGWkes@OhcmUlzJe zTiF|P%`>1<$o$|jkc9cCmD00%KEHyWZ6F-OJN=C?X$H*6g!yZRVs|edqXTY< zgFr4{-kQ0z&4@!W5fNWVbn+{b*0$ki7*6^8(nxK|3=HEoles6Eo_5H~T2Djub$7ru z5rT{fhh5@KmW-vRj?D%8m^$0y)0jG%^9hYi=FSBRMY|(_4gk%c0+|dzM^n9)F4F+% z+jD#t2MCQU0w6xa032|IHGh?a17K>92nc*Z%DOh6#fR(@03i_oE8GDRW&i?%K|l7> zKZ=7O7=ZphfCzVhpmyj#VnG>HJNMo!7J$Y&fIfG?8)~rjeq_QlNLZSL&7x72Q8Nby zBz6E$UI0+%4#<%lXfagP~b|t-{jotY^KjS zj?N$Yd%Fl2Q-k0Og5h^SixEK-5^)IpX?^{RLkq@Y?!E$S9323Vy5S%z_a7rNVf44^ z&v+EUtsFZgr)2EPQ(zg;?HK^zXN)6yelb&Gq8Jn6o>vCodo#Q&hu+RX;0TZhD1VoE zTcHL>0_FlL6b681pd9E_6OcWr_hl0+*;(xTbf}c*1iTGAtuF2rpmfoyjZe)or0q?h z`~C1_P&Af%`7i7(kMccfc-_RtErb~o7FC8Mro~`x844Q1>8N?|-umvr!s!Kk^VF~P zlGB~9M~SpHRfOra%Y$a61WEddv_7n8xm|XDb@~n3N4#e~0c0Qm&;tg*P}YnhcQ!tW zGTN5(G<`4^-mKbmpvv(T@B?&C9StBx?v>hzWM`wR+2wL}A~QkDiD&i?&f{nU2Aytm ny3fZ{tKzPDhtusN+|7OiWZ45YWc*qA00000NkvXXu0mjfY%F{t literal 0 HcmV?d00001 diff --git a/src/tiled/resources/images/24/object-capsule.png b/src/tiled/resources/images/24/object-capsule.png new file mode 100644 index 0000000000000000000000000000000000000000..84f08092b8450e95f02719a6d5edc2ee1e2aa35a GIT binary patch literal 642 zcmV-|0)737P)K)JtrPVHC#k-!~mf$0enaxV9FE5SNftik&ow5MhJF zrV*F8Eo_jWR0sBSti7S#0SsgTG2e=UCi#j2&ChIoaza%ur*V#7t z;_m_1!>IRepjg|-c2|xK^y(oW8wpcAPv*uLIf0prnX{rhcJ0)DXE*Qd9dJ3^jS6p` zv^ldrCY6jUoPOG*l81^{HtL_(lk)08%-!k<@`jdKytTyVw;Wl%T7B)Mbil>1KvZ0+ zD{ypqRN`};vdZnoOi5(5rly7yMiiB}dNSeMmO?Z^Gpx8#Zp>QvAletrKvz$Yy1F- z7~`X{qLDA6#;8G~L=Aqh5gUV|CK`+nP^enb`U)Y@N=VhFC16k+p)JxD`l8S+Ep*#% zcg7Dh-7M?u?6x~KG3h@!na$2UbN~M{_ntZTzzk+Ega0+el)QGTc2Zaa+zAu_nLq|0 z#o(;~JwPYW0My{6j!nWp6)KYxpsIp>l%cW>$eGH7h8XkpAjC72yQ&aEos4Vj0=`n` zFitg43P4x$7{?AcXgv%Udf}266eap|Bnc8yASoTL%7^8*Le?_vUjLaUJV_;#d(K%a z;4}4VB=!Tj^mPpL^)@%Hjo@@QkvhMYg*P51``TU>ulC|_M^`a6EOGqj6xx4J=hTsW z(z4f6vN4CuJm~NA(RQ?$SJxl5Mt~Oi1|XL|Ylr#tMK_Xsf~6%pD643UDFCscreYOK ziYi!Iv5zvKl)erv^0Vjc=eh54uF3uEg&ESg|exSQBZOnScTSS`S0&{95Z7MuirzMFj+dX#g_;nSl0@T6kkjIoVQ3 zoCP2?lVC6nhebvj0KOj0IlF^|2^Gug3+-OfR+Y=!8CxGfEqxn z7Ec|?x13%y2)*iUtRWao14I_!rH(OQ4_V6~Eqnd6kcrEoA=<2k;aCyEi)tjJE-Gkp`O=bLFlo`p-0xnFmXXDj4W=TF>_~ zHUf#Q2=ZvIAELe*U<)uF*~JV(sCbe}=(PGZE`^mlON-mr|fZunsmCO;WM6fGJ{-vVX44pp-lmVt^m!F)ym}S@zM(d~;A%wTECIYgETy)LOp2c8SL)eDI}|t94^er0qE3D-&=Oty zWRRG61%(?9a>ujFNwcmgfwyav-J6}%SEDbRGT?kHu2bm?30w*Hk+)_$E7oPOs6Z>Y zQs#k(p)I5;;O*AFuq_Q*oe@5-6Rjd&G7{EC>VwmBQ&MOelZPC6clx<*Qye z&84b`N7T{5%6K@$^#%ZXpX4stGiOiohId_}|2Nd0dh-c03Q<#UU|`RqBZ0y58`9t8 z%XsFGTL3`EWAa0CO4_^W55DCR7yd#WIAJ_lavo~Z5_&&=VkF@0dvw7&fnD*=ZHWMY z=1tO*_SE^WW^LTz5|>WFaQEc{-1ATq7IONdr$+q!XSQ6uQ+d;hc_a5m%^~zJuB`7P z+uKLfBM;CnG@8 z$=Y>ugxh<^l*W6qFQ$(EeRO%sinTVOj)Lq!am=N4*Vd8V)9t$pKN;T^@*G=;O$eGO zMPjOlVi6#R5f2wJ@!JX5Kl(%Y<(}-J1^$kv+|=t!ZOF4Yxwmqpx8v_`i}(6p3h|f2 zNKafROz0NZc|Z!ADBO(hOAfI8Q0{nrUhbk|fbNqmhi-*;|NZ1BH{6BD{7vWOIuULnkm{ihi9Su7rtD zLWl>fJ5p+6WrMXEx@M&?;-LaC5TS(Gc(f%Gj{qZIVL)7pARye7u - + - + - + eJxjZGBgAAAACAAC - + - + - + - + + + + + + - + diff --git a/src/tiled/tilecollisiondock.cpp b/src/tiled/tilecollisiondock.cpp index e5454d90b8..b9ea933371 100644 --- a/src/tiled/tilecollisiondock.cpp +++ b/src/tiled/tilecollisiondock.cpp @@ -23,6 +23,7 @@ #include "actionmanager.h" #include "addremovemapobject.h" #include "changetileobjectgroup.h" +#include "createcapsuleobjecttool.h" #include "createellipseobjecttool.h" #include "createobjecttool.h" #include "createpointobjecttool.h" @@ -92,6 +93,7 @@ TileCollisionDock::TileCollisionDock(QWidget *parent) CreateObjectTool *rectangleObjectsTool = new CreateRectangleObjectTool(this); CreateObjectTool *pointObjectsTool = new CreatePointObjectTool(this); CreateObjectTool *ellipseObjectsTool = new CreateEllipseObjectTool(this); + CreateObjectTool *capsuleObjectsTool = new CreateCapsuleObjectTool(this); CreateObjectTool *polygonObjectsTool = new CreatePolygonObjectTool(this); CreateObjectTool *templatesTool = new CreateTemplateTool(this); @@ -116,6 +118,7 @@ TileCollisionDock::TileCollisionDock(QWidget *parent) toolsToolBar->addAction(mToolManager->registerTool(rectangleObjectsTool)); toolsToolBar->addAction(mToolManager->registerTool(pointObjectsTool)); toolsToolBar->addAction(mToolManager->registerTool(ellipseObjectsTool)); + toolsToolBar->addAction(mToolManager->registerTool(capsuleObjectsTool)); toolsToolBar->addAction(mToolManager->registerTool(polygonObjectsTool)); toolsToolBar->addAction(mToolManager->registerTool(templatesTool)); toolsToolBar->addSeparator();