From 0b03c73babe80c48c8149ebf6851abb6fccda384 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Mon, 15 Jun 2026 22:07:39 -0500 Subject: [PATCH 01/29] Created new mapItem instance for rendering the tool brush on mapview.qml; Moved the cursor tile coords math to it's own function --- src/tiled/mapeditor.cpp | 16 ++++++++++++++- src/tiled/resources/qml/mapview.qml | 31 ++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 16e9b62687..e9faf86e67 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -373,6 +373,7 @@ void MapEditor::addDocument(Document *document) EditableMap *editableMap = qobject_cast(mapDocument->editable()); engine->rootContext()->setContextProperty(QStringLiteral("mapItemMap"), editableMap); + engine->rootContext()->setContextProperty(QStringLiteral("toolBrushMap"), nullptr); quickWidget->setSource(QUrl(QStringLiteral("qrc:/qml/mapview.qml"))); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); #endif @@ -825,6 +826,16 @@ void MapEditor::setStamp(const TileStamp &stamp) mTilesetDock->selectTilesInStamp(stamp); +#ifdef TILEDQUICK_LIB + if (Preferences::instance()->useNewHardwareRenderer()) + { + QQuickWidget* activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); + QQmlContext* rootContext = activeWidget->engine()->rootContext(); + + rootContext->setContextProperty(QStringLiteral("toolBrushMap"), currentBrush()); + } +#endif + emit currentBrushChanged(); } @@ -1106,7 +1117,10 @@ EditableMap *MapEditor::currentBrush() const auto map = stamp.variations().first().map->clone(); auto editableMap = new EditableMap(std::move(map)); - QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); + + // Disable JavaScriptOwnership for QtQuick rendering + if (!Preferences::instance()->useNewHardwareRenderer()) + QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); return editableMap; } diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index a3ff0211fd..bda424770c 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -55,7 +55,7 @@ Rectangle { } Tiled.MapGridItem { - id: mapGriditem + id: mapGridItem anchors.fill: mapItem tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight); @@ -63,6 +63,28 @@ Rectangle { color: "black" } + + Tiled.MapItem { // Tool Brush + id: toolBrush + anchors.left: mapItem.left + anchors.top: mapItem.top + anchors.leftMargin: (Math.floor(cursorTileCoords().x) - Math.floor(this.map ? this.map.width/2 : 0)) * mapGridItem.tileSize.x + anchors.topMargin: (Math.floor(cursorTileCoords().y) - Math.floor(this.map ? this.map.height/2 : 0)) * mapGridItem.tileSize.y + + map: toolBrushMap; + + visibleArea: { + var scale = mapContainer.scale + // Qt.rect(-mapContainer.x / scale, + // -mapContainer.y / scale, + // mapView.width / scale, + // mapView.height / scale); + Qt.rect(0, + 0, + this.map.width * this.map.tileWidth, + this.map.height * this.map.tileHeight); + } + } } } @@ -130,4 +152,11 @@ Rectangle { mapContainer.y = (mapView.height / 2) - ((mapItem.height * scale) / 2) } } + + function cursorTileCoords() { + var mapRelativeCoords = singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY); + var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y); + + return tileCoords; + } } From 6760a5156cb55133734f25ee4d5e380a17958cce Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Fri, 19 Jun 2026 02:30:50 -0500 Subject: [PATCH 02/29] Fixed tool brush mapitem to no longer cause errors when the map is undefined --- src/tiled/resources/qml/mapview.qml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index bda424770c..2ce66aa420 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -75,14 +75,13 @@ Rectangle { visibleArea: { var scale = mapContainer.scale - // Qt.rect(-mapContainer.x / scale, - // -mapContainer.y / scale, - // mapView.width / scale, - // mapView.height / scale); - Qt.rect(0, - 0, - this.map.width * this.map.tileWidth, - this.map.height * this.map.tileHeight); + if (this.map) + Qt.rect(0, + 0, + this.map.width * this.map.tileWidth, + this.map.height * this.map.tileHeight); + else + Qt.rect(0, 0, 0, 0); } } } From f5309858c67b214ebdabcbacb01e31202fa1350e Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Fri, 19 Jun 2026 03:17:47 -0500 Subject: [PATCH 03/29] mapview.qml updates upon old editor tool usage --- src/libtiledquick/mapitem.cpp | 13 +++++++++++++ src/libtiledquick/mapitem.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index c8cd7b4bd2..2509cd1a93 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -42,6 +42,13 @@ void MapItem::setMap(Tiled::EditableMap *editableMap) mEditableMap = editableMap; mMap = editableMap ? editableMap->map() : nullptr; + + if (editableMap) + { + Tiled::MapDocument *mapDocument = editableMap->mapDocument(); + connect(mapDocument, &Tiled::MapDocument::regionChanged, this, &MapItem::repaintRegion); + } + refresh(); emit mapChanged(); } @@ -170,3 +177,9 @@ void MapItem::refresh() const QRect rect = mRenderer->mapBoundingRect(); setImplicitSize(rect.width(), rect.height()); } + +void MapItem::repaintRegion(const QRegion ®ion, Tiled::TileLayer *tileLayer) +{ + // TODO: update only the region edited + refresh(); +} diff --git a/src/libtiledquick/mapitem.h b/src/libtiledquick/mapitem.h index 2d83a7bc7a..b893d26a6d 100644 --- a/src/libtiledquick/mapitem.h +++ b/src/libtiledquick/mapitem.h @@ -80,6 +80,8 @@ class TILEDQUICK_SHARED_EXPORT MapItem : public QQuickItem private: void refresh(); + void repaintRegion(const QRegion ®ion, Tiled::TileLayer *tileLayer); + Tiled::Map *mMap; Tiled::EditableMap *mEditableMap; QRectF mVisibleArea; From 0005b76295c38debc4ee58a8e797d45be9285e58 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Fri, 19 Jun 2026 20:34:33 -0500 Subject: [PATCH 04/29] Added sizeChanged and tileSizeChanged notify signals to editablemap --- src/tiled/editablemap.cpp | 4 ++++ src/tiled/editablemap.h | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index 3ab640b5be..cf0a974d21 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -497,6 +497,7 @@ void EditableMap::setSize(int width, int height) map()->setWidth(width); map()->setHeight(height); } + emit sizeChanged(); } void EditableMap::setTileWidth(int value) @@ -505,6 +506,7 @@ void EditableMap::setTileWidth(int value) push(new ChangeMapTileSize(doc, QSize(value, tileHeight()))); else if (!checkReadOnly()) map()->setTileWidth(value); + emit tileSizeChanged(); } void EditableMap::setTileHeight(int value) @@ -513,6 +515,7 @@ void EditableMap::setTileHeight(int value) push(new ChangeMapTileSize(doc, QSize(tileWidth(), value))); else if (!checkReadOnly()) map()->setTileHeight(value); + emit tileSizeChanged(); } void EditableMap::setTileSize(int width, int height) @@ -529,6 +532,7 @@ void EditableMap::setTileSize(int width, int height) } else { map()->setTileWidth(width); map()->setTileHeight(height); + emit tileSizeChanged(); } } diff --git a/src/tiled/editablemap.h b/src/tiled/editablemap.h index b3fcf0dfaa..8f36f9660a 100644 --- a/src/tiled/editablemap.h +++ b/src/tiled/editablemap.h @@ -40,11 +40,11 @@ class TILED_EDITOR_EXPORT EditableMap final : public EditableAsset { Q_OBJECT - Q_PROPERTY(int width READ width WRITE setWidth) - Q_PROPERTY(int height READ height WRITE setHeight) - Q_PROPERTY(QSize size READ size) - Q_PROPERTY(int tileWidth READ tileWidth WRITE setTileWidth) - Q_PROPERTY(int tileHeight READ tileHeight WRITE setTileHeight) + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY sizeChanged) + Q_PROPERTY(int height READ height WRITE setHeight NOTIFY sizeChanged) + Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) + Q_PROPERTY(int tileWidth READ tileWidth WRITE setTileWidth NOTIFY tileSizeChanged) + Q_PROPERTY(int tileHeight READ tileHeight WRITE setTileHeight NOTIFY tileSizeChanged) Q_PROPERTY(bool infinite READ infinite WRITE setInfinite) Q_PROPERTY(int hexSideLength READ hexSideLength WRITE setHexSideLength) Q_PROPERTY(StaggerAxis staggerAxis READ staggerAxis WRITE setStaggerAxis) @@ -218,6 +218,8 @@ class TILED_EDITOR_EXPORT EditableMap final : public EditableAsset QSharedPointer createDocument() override; signals: + void sizeChanged(); + void tileSizeChanged(); void currentLayerChanged(); void selectedLayersChanged(); void selectedObjectsChanged(); From 451055f949eabe6c7dd26e57d34fad451c9d37b6 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Sat, 20 Jun 2026 03:12:24 -0500 Subject: [PATCH 05/29] Changed libtiledquick/mapitem::repaintRegion to only update the affected layer --- src/libtiledquick/mapitem.cpp | 13 +++++++++---- src/libtiledquick/tilelayeritem.cpp | 6 ++++-- src/libtiledquick/tilelayeritem.h | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index 2509cd1a93..2351fc4778 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -43,7 +43,7 @@ void MapItem::setMap(Tiled::EditableMap *editableMap) mEditableMap = editableMap; mMap = editableMap ? editableMap->map() : nullptr; - if (editableMap) + if (editableMap && editableMap->mapDocument()) { Tiled::MapDocument *mapDocument = editableMap->mapDocument(); connect(mapDocument, &Tiled::MapDocument::regionChanged, this, &MapItem::repaintRegion); @@ -178,8 +178,13 @@ void MapItem::refresh() setImplicitSize(rect.width(), rect.height()); } -void MapItem::repaintRegion(const QRegion ®ion, Tiled::TileLayer *tileLayer) +void MapItem::repaintRegion(const QRegion &, Tiled::TileLayer *tileLayer) { - // TODO: update only the region edited - refresh(); + for (TileLayerItem *tileLayerItem : std::as_const(mTileLayerItems)) { + if (tileLayer == tileLayerItem->getLayer()) { + // TODO: Update only the region edited + tileLayerItem->update(); + continue; + } + } } diff --git a/src/libtiledquick/tilelayeritem.cpp b/src/libtiledquick/tilelayeritem.cpp index e556aa33a1..cdc185d8a4 100644 --- a/src/libtiledquick/tilelayeritem.cpp +++ b/src/libtiledquick/tilelayeritem.cpp @@ -136,8 +136,6 @@ void TileLayerItem::syncWithTileLayer() setSize(boundingRect.size()); } - - QSGNode *TileLayerItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) { @@ -201,6 +199,10 @@ QSGNode *TileLayerItem::updatePaintNode(QSGNode *node, return node; } +Tiled::TileLayer *TileLayerItem::getLayer() { + return mLayer; +} + void TileLayerItem::updateVisibleTiles() { const MapItem *mapItem = static_cast(parentItem()); diff --git a/src/libtiledquick/tilelayeritem.h b/src/libtiledquick/tilelayeritem.h index 96e07b630b..f77337dd81 100644 --- a/src/libtiledquick/tilelayeritem.h +++ b/src/libtiledquick/tilelayeritem.h @@ -62,6 +62,8 @@ class TILEDQUICK_SHARED_EXPORT TileLayerItem : public QQuickItem QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + Tiled::TileLayer *getLayer(); + public slots: void updateVisibleTiles(); From d703b13c0863b1db1835be58e5189178a34e3282 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 23 Jun 2026 04:44:47 -0500 Subject: [PATCH 06/29] Updated new ToolBrush to work with mouse movement and fill tool Created new signal in mapview.qml to update mapeditor's tool mouse position (Temporary) Moved the ToolBrush map setter code to on mouse move Changed the ToolBrush map setter code to use the currently selected tool's brushItem->map() --- src/tiled/abstracttiletool.h | 8 +++- src/tiled/mapeditor.cpp | 59 ++++++++++++++++++++++++----- src/tiled/mapeditor.h | 5 +++ src/tiled/resources/qml/mapview.qml | 19 +++++++--- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index db8362c3bc..4c4853b5de 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -62,6 +62,11 @@ class AbstractTileTool : public AbstractTool void keyPressed(QKeyEvent *event) override; +#ifdef TILEDQUICK_LIB +signals: + void quickBrushChanged(); +#endif + protected: void mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument) override; @@ -112,8 +117,9 @@ class AbstractTileTool : public AbstractTool * hidden based on whether the mouse is in the scene and whether the * currently selected layer is a tile layer. */ +public: // TODO: REMOVE BrushItem *brushItem() const { return mBrushItem; } - +protected: // TODO: REMOVE /** * Returns the current tile layer, or null if no tile layer is currently * selected. diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index e9faf86e67..e3718c8644 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -89,6 +89,7 @@ #include #include #ifdef TILEDQUICK_LIB +#include #include #endif #include @@ -416,6 +417,14 @@ void MapEditor::setCurrentDocument(Document *document) mCurrentMapDocument->disconnect(this); } +#ifdef TILEDQUICK_LIB + { + QQuickWidget *oldQuickWidget = mViewForMap.value(mapDocument)->quickWidget(); + disconnect(oldQuickWidget->rootObject(), SIGNAL(mouseCoordsChanged(QVariant)), + this, SLOT(onQuickMouseCoordsChanged(QVariant))); + } +#endif + mCurrentMapDocument = mapDocument; MapViewInterface *mapViewInterface = mViewForMap.value(mapDocument); @@ -454,6 +463,16 @@ void MapEditor::setCurrentDocument(Document *document) connect(mCurrentMapDocument, &MapDocument::currentObjectSet, this, [this, mapDocument] { mPropertiesDock->setDocument(mapDocument); }); +#ifdef TILEDQUICK_LIB + if (Preferences::instance()->useNewHardwareRenderer()) { + QQuickWidget *quickWidget = mapViewInterface->quickWidget(); + + if (quickWidget && quickWidget->rootObject()) + connect(quickWidget->rootObject(), SIGNAL(mouseCoordsChanged(QVariant)), + this, SLOT(onQuickMouseCoordsChanged(QVariant))); + } +#endif + mReversingProxyModel->setSourceModel(mapDocument->layerModel()); } else { mReversingProxyModel->setSourceModel(nullptr); @@ -714,9 +733,39 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) } updateActiveUndoStack(); + emit selectedToolChanged(tool); } +#ifdef TILEDQUICK_LIB +void MapEditor::onQuickMouseCoordsChanged(QVariant coords) +{ + QPointF mouseCoords = coords.toPointF(); + selectedTool()->mouseMoved(mouseCoords, Qt::NoModifier); + + + // TODO: Everything past this line will be moved to a slot for an quickBrushChanged signal + AbstractTileTool *atTool = dynamic_cast(selectedTool()); + + if (atTool && atTool->brushItem()->map()) { + QQuickWidget* activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); + QQmlContext* rootContext = activeWidget->engine()->rootContext(); + QVariant brushMapContext = rootContext->contextProperty(QStringLiteral("toolBrushMap")); + + if (brushMapContext.isValid()) { + EditableMap *oldBrushMap = brushMapContext.value(); + EditableMap *newBrushMap = new EditableMap(atTool->brushItem()->map().get()); + + rootContext->setContextProperty(QStringLiteral("toolBrushMap"), newBrushMap); + + if (oldBrushMap && false) + delete oldBrushMap; + } + } + +} +#endif + void MapEditor::updateActiveUndoStack() { QUndoStack *undoStack = DocumentManager::instance()->undoGroup()->activeStack(); @@ -826,16 +875,6 @@ void MapEditor::setStamp(const TileStamp &stamp) mTilesetDock->selectTilesInStamp(stamp); -#ifdef TILEDQUICK_LIB - if (Preferences::instance()->useNewHardwareRenderer()) - { - QQuickWidget* activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); - QQmlContext* rootContext = activeWidget->engine()->rootContext(); - - rootContext->setContextProperty(QStringLiteral("toolBrushMap"), currentBrush()); - } -#endif - emit currentBrushChanged(); } diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index cfebf662b5..9faca70384 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -143,6 +143,11 @@ class MapEditor final : public Editor void currentWangColorIndexChanged(int colorIndex); void selectedToolChanged(AbstractTool *tool); +#ifdef TILEDQUICK_LIB +public slots: + void onQuickMouseCoordsChanged(QVariant coords); +#endif + private: void onSelectedToolChanged(AbstractTool *tool); void currentDocumentChanged(Document *document); diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index 2ce66aa420..ef4cbb12bb 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -68,8 +68,6 @@ Rectangle { id: toolBrush anchors.left: mapItem.left anchors.top: mapItem.top - anchors.leftMargin: (Math.floor(cursorTileCoords().x) - Math.floor(this.map ? this.map.width/2 : 0)) * mapGridItem.tileSize.x - anchors.topMargin: (Math.floor(cursorTileCoords().y) - Math.floor(this.map ? this.map.height/2 : 0)) * mapGridItem.tileSize.y map: toolBrushMap; @@ -89,6 +87,7 @@ Rectangle { DragArea { id: singleFingerPanArea + objectName: "singleFingerPanArea" anchors.fill: parent onDragged: function(dx, dy) { @@ -153,9 +152,19 @@ Rectangle { } function cursorTileCoords() { - var mapRelativeCoords = singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY); - var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y); + var tileCoords = mapItem.screenToTileCoords(widget.mapRelativeCoords.x, widget.mapRelativeCoords.y); - return tileCoords; + return tileCoords + } + + signal mouseCoordsChanged(var coords) + + property var mapRelativeCoords: { + var mapRelativeCoords = singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY) + return mapRelativeCoords + } + + onMapRelativeCoordsChanged: { + mouseCoordsChanged(mapRelativeCoords) } } From 85dcc284552ed1ac4adc844347343ddbeed27bb2 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 23 Jun 2026 19:41:03 -0500 Subject: [PATCH 07/29] Moved quick brush changes to onQuickBrushChanged and set up emits in related tools --- src/tiled/abstracttilefilltool.cpp | 4 ++++ src/tiled/abstracttiletool.h | 4 ++-- src/tiled/mapeditor.cpp | 31 +++++++++++++++++++++++------- src/tiled/mapeditor.h | 1 + src/tiled/stampbrush.cpp | 4 ++++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index b4a33785b2..752e9de683 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -231,6 +231,10 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) brushItem()->setMap(preview); mPreviewMap = preview; + +#ifdef TILEDQUICK_LIB + emit quickBrushChanged(); +#endif } bool AbstractTileFillTool::applyPreview(const QString &text) diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 4c4853b5de..6b45a739a7 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -117,9 +117,9 @@ class AbstractTileTool : public AbstractTool * hidden based on whether the mouse is in the scene and whether the * currently selected layer is a tile layer. */ -public: // TODO: REMOVE +public: // TODO: MOVE BrushItem *brushItem() const { return mBrushItem; } -protected: // TODO: REMOVE +protected: // TODO: MOVE /** * Returns the current tile layer, or null if no tile layer is currently * selected. diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index e3718c8644..25f227aa2d 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -710,6 +710,13 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) if (mSelectedTool) { disconnect(mSelectedTool, &AbstractTool::cursorChanged, this, &MapEditor::cursorChanged); +#ifdef TILEDQUICK_LIB + AbstractTileTool *atTool = dynamic_cast(mSelectedTool); + if (atTool) { + disconnect(atTool, &AbstractTileTool::quickBrushChanged, + this, &MapEditor::onQuickBrushChanged); + } +#endif } mSelectedTool = tool; @@ -728,6 +735,13 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) if (tool) { connect(tool, &AbstractTool::cursorChanged, this, &MapEditor::cursorChanged); +#ifdef TILEDQUICK_LIB + AbstractTileTool *atTool = dynamic_cast(tool); + if (atTool) { + connect(atTool, &AbstractTileTool::quickBrushChanged, + this, &MapEditor::onQuickBrushChanged); + } +#endif tool->populateToolBar(mToolSpecificToolBar); } @@ -742,27 +756,30 @@ void MapEditor::onQuickMouseCoordsChanged(QVariant coords) { QPointF mouseCoords = coords.toPointF(); selectedTool()->mouseMoved(mouseCoords, Qt::NoModifier); +} - - // TODO: Everything past this line will be moved to a slot for an quickBrushChanged signal +void MapEditor::onQuickBrushChanged() +{ AbstractTileTool *atTool = dynamic_cast(selectedTool()); if (atTool && atTool->brushItem()->map()) { - QQuickWidget* activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); - QQmlContext* rootContext = activeWidget->engine()->rootContext(); + QQuickWidget *activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); + QQmlContext *rootContext = activeWidget->engine()->rootContext(); QVariant brushMapContext = rootContext->contextProperty(QStringLiteral("toolBrushMap")); if (brushMapContext.isValid()) { EditableMap *oldBrushMap = brushMapContext.value(); - EditableMap *newBrushMap = new EditableMap(atTool->brushItem()->map().get()); + if (oldBrushMap && oldBrushMap->map() == atTool->brushItem()->map().get()) + return; + + EditableMap *newBrushMap = new EditableMap(atTool->brushItem()->map().get()); rootContext->setContextProperty(QStringLiteral("toolBrushMap"), newBrushMap); - if (oldBrushMap && false) + if (oldBrushMap) delete oldBrushMap; } } - } #endif diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 9faca70384..1fd1202425 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -146,6 +146,7 @@ class MapEditor final : public Editor #ifdef TILEDQUICK_LIB public slots: void onQuickMouseCoordsChanged(QVariant coords); + void onQuickBrushChanged(); #endif private: diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index bfdd6ad500..e027de0602 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -676,6 +676,10 @@ void StampBrush::updatePreview(QPoint tilePos) } brushItem()->setMap(mPreviewMap, tileRegion); + +#ifdef TILEDQUICK_LIB + emit quickBrushChanged(); +#endif } void StampBrush::setRandom(bool value) From 833bbf03e2075e00b6120646b03da835276818a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 24 Jun 2026 13:38:32 +0200 Subject: [PATCH 08/29] Don't link to the tiledquickplugin This Depends is only there to make sure the plugin is compiled, but the plugin is loaded dynamically as a QML module. --- src/tiledapp/tiledapp.qbs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tiledapp/tiledapp.qbs b/src/tiledapp/tiledapp.qbs index 6682a60438..e717ec62d4 100644 --- a/src/tiledapp/tiledapp.qbs +++ b/src/tiledapp/tiledapp.qbs @@ -14,6 +14,10 @@ TiledQtGuiApplication { name: "tiledquickplugin" condition: Qt.core.version !== undefined && Utilities.versionCompare(Qt.core.version, "6.5") >= 0 + + // Disable linking against the plugin, as it is loaded dynamically at + // runtime. The Depends is only used to ensure that the plugin is built. + cpp.link: false } Depends { name: "ib"; condition: qbs.targetOS.contains("macos") } Depends { name: "xcode"; condition: qbs.targetOS.contains("macos") } From b07fc431940711a9282e8bd555b5f51456650bb4 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 24 Jun 2026 13:55:45 -0500 Subject: [PATCH 09/29] Changed editablemap's size signals to be sent properly; MapItem now refreshes upon map resize --- src/libtiledquick/mapitem.cpp | 1 + src/tiled/editablemap.cpp | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index 2351fc4778..e94993f399 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -47,6 +47,7 @@ void MapItem::setMap(Tiled::EditableMap *editableMap) { Tiled::MapDocument *mapDocument = editableMap->mapDocument(); connect(mapDocument, &Tiled::MapDocument::regionChanged, this, &MapItem::repaintRegion); + connect(mapDocument, &Tiled::MapDocument::mapResized, this, &MapItem::refresh); } refresh(); diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index cf0a974d21..f7b47019a4 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -497,7 +497,6 @@ void EditableMap::setSize(int width, int height) map()->setWidth(width); map()->setHeight(height); } - emit sizeChanged(); } void EditableMap::setTileWidth(int value) @@ -506,7 +505,6 @@ void EditableMap::setTileWidth(int value) push(new ChangeMapTileSize(doc, QSize(value, tileHeight()))); else if (!checkReadOnly()) map()->setTileWidth(value); - emit tileSizeChanged(); } void EditableMap::setTileHeight(int value) @@ -515,7 +513,6 @@ void EditableMap::setTileHeight(int value) push(new ChangeMapTileSize(doc, QSize(tileWidth(), value))); else if (!checkReadOnly()) map()->setTileHeight(value); - emit tileSizeChanged(); } void EditableMap::setTileSize(int width, int height) @@ -532,7 +529,6 @@ void EditableMap::setTileSize(int width, int height) } else { map()->setTileWidth(width); map()->setTileHeight(height); - emit tileSizeChanged(); } } @@ -727,6 +723,7 @@ void EditableMap::setDocument(Document *document) if (auto doc = mapDocument()) { connect(doc, &Document::fileNameChanged, this, &EditableAsset::fileNameChanged); connect(doc, &Document::changed, this, &EditableMap::documentChanged); + connect(doc, &MapDocument::mapResized, this, &EditableMap::sizeChanged); connect(doc, &MapDocument::layerAdded, this, &EditableMap::attachLayer); connect(doc, &MapDocument::layerRemoved, this, &EditableMap::detachLayer); @@ -751,10 +748,20 @@ void EditableMap::documentChanged(const ChangeEvent &change) case ChangeEvent::DocumentReloaded: setObject(mapDocument()->map()); break; - case ChangeEvent::MapChanged: - if (static_cast(change).property == Map::OrientationProperty) + case ChangeEvent::MapChanged: { + auto &mapChange = static_cast(change); + switch (mapChange.property) { + case Map::TileSizeProperty: + emit tileSizeChanged(); + break; + case Map::OrientationProperty: mRenderer.reset(); + break; + default: + break; + } break; + } case ChangeEvent::MapObjectsAdded: attachMapObjects(static_cast(change).mapObjects); break; From 9ca0d815979735aad1b255e3aff7aa89baacfbb3 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:06:33 -0500 Subject: [PATCH 10/29] Added disconnects for libtiledquick/mapitem's setmap connections --- src/libtiledquick/mapitem.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index e94993f399..6d25acb310 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -40,16 +40,21 @@ void MapItem::setMap(Tiled::EditableMap *editableMap) if (mEditableMap == editableMap) return; - mEditableMap = editableMap; - mMap = editableMap ? editableMap->map() : nullptr; + if (mEditableMap && mEditableMap->mapDocument()) { + Tiled::MapDocument *oldMapDocument = mEditableMap->mapDocument(); + disconnect(oldMapDocument, &Tiled::MapDocument::regionChanged, this, &MapItem::repaintRegion); + disconnect(oldMapDocument, &Tiled::MapDocument::mapResized, this, &MapItem::refresh); + } - if (editableMap && editableMap->mapDocument()) - { + if (editableMap && editableMap->mapDocument()) { Tiled::MapDocument *mapDocument = editableMap->mapDocument(); connect(mapDocument, &Tiled::MapDocument::regionChanged, this, &MapItem::repaintRegion); connect(mapDocument, &Tiled::MapDocument::mapResized, this, &MapItem::refresh); } + mEditableMap = editableMap; + mMap = editableMap ? editableMap->map() : nullptr; + refresh(); emit mapChanged(); } From 40055c8020d3e160aa687932f0b6e8b6db91c89e Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:13:07 -0500 Subject: [PATCH 11/29] Small fixes for review comments initialize libtiledquick/mapitem's mEditableMap to nullptr so that it can be read during setmap removed unnecessary lines from mapview.qml renamed tilelayeritem's getlayer to layer added selectedTool boolean check to mapeditor::onquickmousecoordschanged changed dynamic_casts to qobject_casts --- src/libtiledquick/mapitem.cpp | 3 ++- src/libtiledquick/tilelayeritem.cpp | 2 +- src/libtiledquick/tilelayeritem.h | 2 +- src/tiled/mapeditor.cpp | 9 ++++++--- src/tiled/resources/qml/mapview.qml | 2 -- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index 6d25acb310..53debd4d5b 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -30,6 +30,7 @@ using namespace TiledQuick; MapItem::MapItem(QQuickItem *parent) : QQuickItem(parent) + , mEditableMap(nullptr) { } @@ -187,7 +188,7 @@ void MapItem::refresh() void MapItem::repaintRegion(const QRegion &, Tiled::TileLayer *tileLayer) { for (TileLayerItem *tileLayerItem : std::as_const(mTileLayerItems)) { - if (tileLayer == tileLayerItem->getLayer()) { + if (tileLayer == tileLayerItem->layer()) { // TODO: Update only the region edited tileLayerItem->update(); continue; diff --git a/src/libtiledquick/tilelayeritem.cpp b/src/libtiledquick/tilelayeritem.cpp index cdc185d8a4..185eae6ab6 100644 --- a/src/libtiledquick/tilelayeritem.cpp +++ b/src/libtiledquick/tilelayeritem.cpp @@ -199,7 +199,7 @@ QSGNode *TileLayerItem::updatePaintNode(QSGNode *node, return node; } -Tiled::TileLayer *TileLayerItem::getLayer() { +Tiled::TileLayer *TileLayerItem::layer() { return mLayer; } diff --git a/src/libtiledquick/tilelayeritem.h b/src/libtiledquick/tilelayeritem.h index f77337dd81..02626f5c94 100644 --- a/src/libtiledquick/tilelayeritem.h +++ b/src/libtiledquick/tilelayeritem.h @@ -62,7 +62,7 @@ class TILEDQUICK_SHARED_EXPORT TileLayerItem : public QQuickItem QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; - Tiled::TileLayer *getLayer(); + Tiled::TileLayer *layer(); public slots: void updateVisibleTiles(); diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 25f227aa2d..94d06efa7b 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -711,7 +711,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) disconnect(mSelectedTool, &AbstractTool::cursorChanged, this, &MapEditor::cursorChanged); #ifdef TILEDQUICK_LIB - AbstractTileTool *atTool = dynamic_cast(mSelectedTool); + AbstractTileTool *atTool = qobject_cast(mSelectedTool); if (atTool) { disconnect(atTool, &AbstractTileTool::quickBrushChanged, this, &MapEditor::onQuickBrushChanged); @@ -736,7 +736,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) connect(tool, &AbstractTool::cursorChanged, this, &MapEditor::cursorChanged); #ifdef TILEDQUICK_LIB - AbstractTileTool *atTool = dynamic_cast(tool); + AbstractTileTool *atTool = qobject_cast(tool); if (atTool) { connect(atTool, &AbstractTileTool::quickBrushChanged, this, &MapEditor::onQuickBrushChanged); @@ -754,13 +754,16 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) #ifdef TILEDQUICK_LIB void MapEditor::onQuickMouseCoordsChanged(QVariant coords) { + if (!selectedTool()) + return; + QPointF mouseCoords = coords.toPointF(); selectedTool()->mouseMoved(mouseCoords, Qt::NoModifier); } void MapEditor::onQuickBrushChanged() { - AbstractTileTool *atTool = dynamic_cast(selectedTool()); + AbstractTileTool *atTool = qobject_cast(selectedTool()); if (atTool && atTool->brushItem()->map()) { QQuickWidget *activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index ef4cbb12bb..4cce94d4a2 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -72,7 +72,6 @@ Rectangle { map: toolBrushMap; visibleArea: { - var scale = mapContainer.scale if (this.map) Qt.rect(0, 0, @@ -87,7 +86,6 @@ Rectangle { DragArea { id: singleFingerPanArea - objectName: "singleFingerPanArea" anchors.fill: parent onDragged: function(dx, dy) { From 2e3f22b82a0f12e537f84eb4517de3e538c95630 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 24 Jun 2026 20:43:15 -0500 Subject: [PATCH 12/29] Renamed quickBrushChanged signal to brushMapChanged; Javascript ownership in currentBrush() now applies to both renderers --- src/tiled/abstracttilefilltool.cpp | 2 +- src/tiled/abstracttiletool.h | 2 +- src/tiled/mapeditor.cpp | 14 ++++++-------- src/tiled/mapeditor.h | 2 +- src/tiled/stampbrush.cpp | 4 +--- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index 752e9de683..5ae3383762 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -233,7 +233,7 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) mPreviewMap = preview; #ifdef TILEDQUICK_LIB - emit quickBrushChanged(); + emit brushMapChanged(); #endif } diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 6b45a739a7..e546dd96f7 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -64,7 +64,7 @@ class AbstractTileTool : public AbstractTool #ifdef TILEDQUICK_LIB signals: - void quickBrushChanged(); + void brushMapChanged(); #endif protected: diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 94d06efa7b..99e9936cdb 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -713,8 +713,8 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) #ifdef TILEDQUICK_LIB AbstractTileTool *atTool = qobject_cast(mSelectedTool); if (atTool) { - disconnect(atTool, &AbstractTileTool::quickBrushChanged, - this, &MapEditor::onQuickBrushChanged); + disconnect(atTool, &AbstractTileTool::brushMapChanged, + this, &MapEditor::onBrushMapChanged); } #endif } @@ -738,8 +738,8 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) #ifdef TILEDQUICK_LIB AbstractTileTool *atTool = qobject_cast(tool); if (atTool) { - connect(atTool, &AbstractTileTool::quickBrushChanged, - this, &MapEditor::onQuickBrushChanged); + connect(atTool, &AbstractTileTool::brushMapChanged, + this, &MapEditor::onBrushMapChanged); } #endif @@ -761,7 +761,7 @@ void MapEditor::onQuickMouseCoordsChanged(QVariant coords) selectedTool()->mouseMoved(mouseCoords, Qt::NoModifier); } -void MapEditor::onQuickBrushChanged() +void MapEditor::onBrushMapChanged() { AbstractTileTool *atTool = qobject_cast(selectedTool()); @@ -1177,9 +1177,7 @@ EditableMap *MapEditor::currentBrush() const auto map = stamp.variations().first().map->clone(); auto editableMap = new EditableMap(std::move(map)); - // Disable JavaScriptOwnership for QtQuick rendering - if (!Preferences::instance()->useNewHardwareRenderer()) - QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); return editableMap; } diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 1fd1202425..2131440d3a 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -146,7 +146,7 @@ class MapEditor final : public Editor #ifdef TILEDQUICK_LIB public slots: void onQuickMouseCoordsChanged(QVariant coords); - void onQuickBrushChanged(); + void onBrushMapChanged(); #endif private: diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index e027de0602..55a6d223bb 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -677,9 +677,7 @@ void StampBrush::updatePreview(QPoint tilePos) brushItem()->setMap(mPreviewMap, tileRegion); -#ifdef TILEDQUICK_LIB - emit quickBrushChanged(); -#endif + emit brushMapChanged(); } void StampBrush::setRandom(bool value) From 8632491f12fbb5eed90d5bbf3f2643e4a9eaa754 Mon Sep 17 00:00:00 2001 From: UltraDagon <43974011+UltraDagon@users.noreply.github.com> Date: Fri, 26 Jun 2026 02:32:33 -0500 Subject: [PATCH 13/29] Partially implemented requested brushItem->map() and toolBrushMap changes Added MapEditor::tileEditPreview() getter function Added AbstractTileTool::brushMap() getter function Moved toolBrushMap to a property var called toolPreviewMap to fix a garbage collector bug Need to add the Q_PROPERTY to mapeditor for the tileEditPreview. Pushed so that I can resume work on a difference machine. --- src/tiled/abstracttilefilltool.cpp | 2 -- src/tiled/abstracttiletool.cpp | 5 ++++ src/tiled/abstracttiletool.h | 7 +++--- src/tiled/mapeditor.cpp | 37 +++++++++++++++++------------ src/tiled/mapeditor.h | 2 ++ src/tiled/resources/qml/mapview.qml | 5 +++- 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index 5ae3383762..a82a7bf310 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -232,9 +232,7 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) brushItem()->setMap(preview); mPreviewMap = preview; -#ifdef TILEDQUICK_LIB emit brushMapChanged(); -#endif } bool AbstractTileFillTool::applyPreview(const QString &text) diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 56c6f9714d..e2059e3cb2 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -58,6 +58,11 @@ AbstractTileTool::~AbstractTileTool() delete mBrushItem; } +const SharedMap &AbstractTileTool::brushMap() const +{ + return brushItem()->map(); +} + void AbstractTileTool::activate(MapScene *scene) { scene->addItem(mBrushItem); diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index e546dd96f7..46c39aa543 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -52,6 +52,8 @@ class AbstractTileTool : public AbstractTool ~AbstractTileTool() override; + const SharedMap &brushMap() const; + void activate(MapScene *scene) override; void deactivate(MapScene *scene) override; @@ -62,10 +64,8 @@ class AbstractTileTool : public AbstractTool void keyPressed(QKeyEvent *event) override; -#ifdef TILEDQUICK_LIB signals: void brushMapChanged(); -#endif protected: void mapDocumentChanged(MapDocument *oldDocument, @@ -117,9 +117,8 @@ class AbstractTileTool : public AbstractTool * hidden based on whether the mouse is in the scene and whether the * currently selected layer is a tile layer. */ -public: // TODO: MOVE BrushItem *brushItem() const { return mBrushItem; } -protected: // TODO: MOVE + /** * Returns the current tile layer, or null if no tile layer is currently * selected. diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 99e9936cdb..3b16284acd 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -763,26 +763,21 @@ void MapEditor::onQuickMouseCoordsChanged(QVariant coords) void MapEditor::onBrushMapChanged() { - AbstractTileTool *atTool = qobject_cast(selectedTool()); + EditableMap *brushMap = tileEditPreview(); - if (atTool && atTool->brushItem()->map()) { - QQuickWidget *activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); - QQmlContext *rootContext = activeWidget->engine()->rootContext(); - QVariant brushMapContext = rootContext->contextProperty(QStringLiteral("toolBrushMap")); + QQuickWidget *activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); + QQmlContext *rootContext = activeWidget->engine()->rootContext(); + QVariant brushMapContext = rootContext->contextProperty(QStringLiteral("toolBrushMap")); - if (brushMapContext.isValid()) { - EditableMap *oldBrushMap = brushMapContext.value(); + if (!brushMap || !brushMapContext.isValid()) + return; - if (oldBrushMap && oldBrushMap->map() == atTool->brushItem()->map().get()) - return; + EditableMap *oldBrushMap = brushMapContext.value(); - EditableMap *newBrushMap = new EditableMap(atTool->brushItem()->map().get()); - rootContext->setContextProperty(QStringLiteral("toolBrushMap"), newBrushMap); + if (oldBrushMap && oldBrushMap->map() == brushMap->map()) + return; - if (oldBrushMap) - delete oldBrushMap; - } - } + rootContext->setContextProperty(QStringLiteral("toolBrushMap"), brushMap); } #endif @@ -1181,6 +1176,18 @@ EditableMap *MapEditor::currentBrush() const return editableMap; } +EditableMap *MapEditor::tileEditPreview() const +{ + AbstractTileTool *atTool = qobject_cast(selectedTool()); + if (!atTool || !atTool->brushMap()) + return nullptr; + + EditableMap *editableMap = new EditableMap(atTool->brushMap().get()); + + QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); + return editableMap; +} + void MapEditor::setCurrentBrush(EditableMap *editableMap) { if (!editableMap) { diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 2131440d3a..ab7ffb6766 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -121,6 +121,8 @@ class MapEditor final : public Editor EditableMap *currentBrush() const; void setCurrentBrush(EditableMap *editableMap); + EditableMap *tileEditPreview() const; + EditableWangSet *currentWangSet() const; void setCurrentWangSet(EditableWangSet *wangSet); diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index 4cce94d4a2..d561f44922 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -69,7 +69,10 @@ Rectangle { anchors.left: mapItem.left anchors.top: mapItem.top - map: toolBrushMap; + // Save toolBrushMap as a property var so that qml doesn't lose + // track of it during garbage collection + property var toolPreviewMap: toolBrushMap; + map: toolPreviewMap visibleArea: { if (this.map) From e096b8d9f89ede571af0fcd73d1a87807f0c0927 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Sat, 27 Jun 2026 18:08:33 -0500 Subject: [PATCH 14/29] Moved brushChanged logic to a Q_PROPERTY and quickMouseCoordsChanged logic to be updated by mapview.qml --- src/tiled/mapeditor.cpp | 62 +++++------------------------ src/tiled/mapeditor.h | 12 +++--- src/tiled/resources/qml/mapview.qml | 19 ++++----- 3 files changed, 27 insertions(+), 66 deletions(-) diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 3b16284acd..1d7f1db97a 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -373,8 +373,8 @@ void MapEditor::addDocument(Document *document) EditableMap *editableMap = qobject_cast(mapDocument->editable()); + engine->rootContext()->setContextProperty(QStringLiteral("mapEditorInstance"), this); engine->rootContext()->setContextProperty(QStringLiteral("mapItemMap"), editableMap); - engine->rootContext()->setContextProperty(QStringLiteral("toolBrushMap"), nullptr); quickWidget->setSource(QUrl(QStringLiteral("qrc:/qml/mapview.qml"))); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); #endif @@ -417,14 +417,6 @@ void MapEditor::setCurrentDocument(Document *document) mCurrentMapDocument->disconnect(this); } -#ifdef TILEDQUICK_LIB - { - QQuickWidget *oldQuickWidget = mViewForMap.value(mapDocument)->quickWidget(); - disconnect(oldQuickWidget->rootObject(), SIGNAL(mouseCoordsChanged(QVariant)), - this, SLOT(onQuickMouseCoordsChanged(QVariant))); - } -#endif - mCurrentMapDocument = mapDocument; MapViewInterface *mapViewInterface = mViewForMap.value(mapDocument); @@ -463,16 +455,6 @@ void MapEditor::setCurrentDocument(Document *document) connect(mCurrentMapDocument, &MapDocument::currentObjectSet, this, [this, mapDocument] { mPropertiesDock->setDocument(mapDocument); }); -#ifdef TILEDQUICK_LIB - if (Preferences::instance()->useNewHardwareRenderer()) { - QQuickWidget *quickWidget = mapViewInterface->quickWidget(); - - if (quickWidget && quickWidget->rootObject()) - connect(quickWidget->rootObject(), SIGNAL(mouseCoordsChanged(QVariant)), - this, SLOT(onQuickMouseCoordsChanged(QVariant))); - } -#endif - mReversingProxyModel->setSourceModel(mapDocument->layerModel()); } else { mReversingProxyModel->setSourceModel(nullptr); @@ -714,7 +696,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) AbstractTileTool *atTool = qobject_cast(mSelectedTool); if (atTool) { disconnect(atTool, &AbstractTileTool::brushMapChanged, - this, &MapEditor::onBrushMapChanged); + this, &MapEditor::tileEditPreviewChanged); } #endif } @@ -739,7 +721,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) AbstractTileTool *atTool = qobject_cast(tool); if (atTool) { connect(atTool, &AbstractTileTool::brushMapChanged, - this, &MapEditor::onBrushMapChanged); + this, &MapEditor::tileEditPreviewChanged); } #endif @@ -751,36 +733,6 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) emit selectedToolChanged(tool); } -#ifdef TILEDQUICK_LIB -void MapEditor::onQuickMouseCoordsChanged(QVariant coords) -{ - if (!selectedTool()) - return; - - QPointF mouseCoords = coords.toPointF(); - selectedTool()->mouseMoved(mouseCoords, Qt::NoModifier); -} - -void MapEditor::onBrushMapChanged() -{ - EditableMap *brushMap = tileEditPreview(); - - QQuickWidget *activeWidget = mViewForMap.value(mCurrentMapDocument)->quickWidget(); - QQmlContext *rootContext = activeWidget->engine()->rootContext(); - QVariant brushMapContext = rootContext->contextProperty(QStringLiteral("toolBrushMap")); - - if (!brushMap || !brushMapContext.isValid()) - return; - - EditableMap *oldBrushMap = brushMapContext.value(); - - if (oldBrushMap && oldBrushMap->map() == brushMap->map()) - return; - - rootContext->setContextProperty(QStringLiteral("toolBrushMap"), brushMap); -} -#endif - void MapEditor::updateActiveUndoStack() { QUndoStack *undoStack = DocumentManager::instance()->undoGroup()->activeStack(); @@ -1235,6 +1187,14 @@ void MapEditor::setSelectedTool(AbstractTool *tool) mToolManager->selectTool(tool); } +void MapEditor::setQuickMouseCoords(QPointF coords) +{ + if (!selectedTool()) + return; + + selectedTool()->mouseMoved(coords, Qt::NoModifier); +} + AbstractTool *MapEditor::tool(const QByteArray &id) const { return mToolManager->findTool(id); diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index ab7ffb6766..2fd2cf306a 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -75,6 +75,7 @@ class MapEditor final : public Editor Q_PROPERTY(Tiled::TilesetDock *tilesetsView READ tilesetDock CONSTANT) Q_PROPERTY(Tiled::EditableMap *currentBrush READ currentBrush WRITE setCurrentBrush NOTIFY currentBrushChanged) + Q_PROPERTY(Tiled::EditableMap *tileEditPreview READ tileEditPreview NOTIFY tileEditPreviewChanged) Q_PROPERTY(Tiled::EditableWangSet *currentWangSet READ currentWangSet WRITE setCurrentWangSet NOTIFY currentWangSetChanged) Q_PROPERTY(int currentWangColorIndex READ currentWangColorIndex WRITE setCurrentWangColorIndex NOTIFY currentWangColorIndexChanged) Q_PROPERTY(Tiled::MapView *currentMapView READ currentMapView CONSTANT) @@ -137,20 +138,19 @@ class MapEditor final : public Editor AbstractTool *selectedTool() const; void setSelectedTool(AbstractTool *tool); +#ifdef TILEDQUICK_LIB + Q_INVOKABLE void setQuickMouseCoords(QPointF coords); +#endif + Q_INVOKABLE AbstractTool *tool(const QByteArray &id) const; signals: void currentBrushChanged(); + void tileEditPreviewChanged(); void currentWangSetChanged(); void currentWangColorIndexChanged(int colorIndex); void selectedToolChanged(AbstractTool *tool); -#ifdef TILEDQUICK_LIB -public slots: - void onQuickMouseCoordsChanged(QVariant coords); - void onBrushMapChanged(); -#endif - private: void onSelectedToolChanged(AbstractTool *tool); void currentDocumentChanged(Document *document); diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index d561f44922..d5e8fed987 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -11,6 +11,8 @@ Rectangle { anchors.fill: parent visible: true + property var mapEditor: mapEditorInstance + Item { id: mapView @@ -71,15 +73,16 @@ Rectangle { // Save toolBrushMap as a property var so that qml doesn't lose // track of it during garbage collection - property var toolPreviewMap: toolBrushMap; + property var toolPreviewMap: mapEditor.tileEditPreview map: toolPreviewMap visibleArea: { + var scale = mapContainer.scale if (this.map) - Qt.rect(0, - 0, - this.map.width * this.map.tileWidth, - this.map.height * this.map.tileHeight); + Qt.rect(-mapContainer.x / scale, + -mapContainer.y / scale, + mapView.width / scale, + mapView.height / scale); else Qt.rect(0, 0, 0, 0); } @@ -153,19 +156,17 @@ Rectangle { } function cursorTileCoords() { - var tileCoords = mapItem.screenToTileCoords(widget.mapRelativeCoords.x, widget.mapRelativeCoords.y); + var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y); return tileCoords } - signal mouseCoordsChanged(var coords) - property var mapRelativeCoords: { var mapRelativeCoords = singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY) return mapRelativeCoords } onMapRelativeCoordsChanged: { - mouseCoordsChanged(mapRelativeCoords) + mapEditor.setQuickMouseCoords(mapRelativeCoords) } } From 8c3e48fcf989bf5829cbeaada9dfb0e0e0188664 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Sat, 27 Jun 2026 18:47:13 -0500 Subject: [PATCH 15/29] Fixed ifdefs for TILEDQUICK_LIB --- src/tiled/mapeditor.cpp | 8 ++++++-- src/tiled/mapeditor.h | 2 ++ src/tiled/mapviewinterface.cpp | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 1d7f1db97a..13195e01b4 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -304,7 +304,9 @@ MapEditor::MapEditor(QObject *parent) Preferences *prefs = Preferences::instance(); connect(prefs, &Preferences::useOpenGLChanged, this, &MapEditor::setUseOpenGL); +#ifdef TILEDQUICK_LIB connect(prefs, &Preferences::useNewHardwareRendererChanged, this, &MapEditor::setUseNewHardwareRenderer); +#endif connect(prefs, &Preferences::languageChanged, this, &MapEditor::retranslateUi); connect(prefs, &Preferences::showTileCollisionShapesChanged, this, &MapEditor::showTileCollisionShapesChanged); @@ -1070,9 +1072,9 @@ void MapEditor::setUseOpenGL(bool useOpenGL) } } +#ifdef TILEDQUICK_LIB void MapEditor::setUseNewHardwareRenderer(bool useNewHardwareRenderer) { -#ifdef TILEDQUICK_LIB for (MapViewInterface *mapViewInterface : std::as_const(mViewForMap)) { mWidgetStack->addWidget(mapViewInterface->getWidget()); @@ -1084,8 +1086,8 @@ void MapEditor::setUseNewHardwareRenderer(bool useNewHardwareRenderer) } mWidgetStack->setCurrentWidget(mViewForMap.value(mCurrentMapDocument)->getWidget()); -#endif } +#endif void MapEditor::retranslateUi() { @@ -1187,6 +1189,7 @@ void MapEditor::setSelectedTool(AbstractTool *tool) mToolManager->selectTool(tool); } +#ifdef TILEDQUICK_LIB void MapEditor::setQuickMouseCoords(QPointF coords) { if (!selectedTool()) @@ -1194,6 +1197,7 @@ void MapEditor::setQuickMouseCoords(QPointF coords) selectedTool()->mouseMoved(coords, Qt::NoModifier); } +#endif AbstractTool *MapEditor::tool(const QByteArray &id) const { diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 2fd2cf306a..38be5f5939 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -178,7 +178,9 @@ class MapEditor final : public Editor void setupQuickStamps(); void setUseOpenGL(bool useOpenGL); +#ifdef TILEDQUICK_LIB void setUseNewHardwareRenderer(bool useNewHardwareRenderer); +#endif void retranslateUi(); void showTileCollisionShapesChanged(bool enabled); void parallaxEnabledChanged(bool enabled); diff --git a/src/tiled/mapviewinterface.cpp b/src/tiled/mapviewinterface.cpp index 53034f6d9b..48a4b92c7f 100644 --- a/src/tiled/mapviewinterface.cpp +++ b/src/tiled/mapviewinterface.cpp @@ -24,10 +24,12 @@ namespace Tiled { +#ifdef TILEDQUICK_LIB static bool quickEnabled() { return Preferences::instance()->useNewHardwareRenderer(); } +#endif MapViewInterface::MapViewInterface(QWidget *parent) : QObject{parent} From 4cbb04606e567ca438a9c80dd01d200e8829133b Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:33:07 -0500 Subject: [PATCH 16/29] Created template classes and setup for regionOverlay --- src/libtiledquick/libtiledquick.qbs | 4 + src/libtiledquick/mapgriditem.cpp | 33 +++---- src/libtiledquick/regionoverlay.cpp | 101 ++++++++++++++++++++++ src/libtiledquick/regionoverlay.h | 69 +++++++++++++++ src/tiled/resources/qml/mapview.qml | 5 ++ src/tiledquickplugin/tiledquickplugin.cpp | 2 + 6 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 src/libtiledquick/regionoverlay.cpp create mode 100644 src/libtiledquick/regionoverlay.h diff --git a/src/libtiledquick/libtiledquick.qbs b/src/libtiledquick/libtiledquick.qbs index cc64210a1a..3a5721a188 100644 --- a/src/libtiledquick/libtiledquick.qbs +++ b/src/libtiledquick/libtiledquick.qbs @@ -38,6 +38,8 @@ DynamicLibrary { "mapitem.h", "maploader.cpp", "maploader.h", + "regionoverlay.cpp", + "regionoverlay.h", "tiledquick_global.h", "tilelayeritem.cpp", "tilelayeritem.h", @@ -71,6 +73,8 @@ DynamicLibrary { cpp.includePaths: exportingProduct.sourceDirectory } + Depends { name: "Qt.core" } + install: !qbs.targetOS.contains("darwin") installDir: { if (qbs.targetOS.contains("windows")) diff --git a/src/libtiledquick/mapgriditem.cpp b/src/libtiledquick/mapgriditem.cpp index e7c158a96d..208ed62d3e 100644 --- a/src/libtiledquick/mapgriditem.cpp +++ b/src/libtiledquick/mapgriditem.cpp @@ -68,11 +68,12 @@ QSGNode *MapGridItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode void MapGridItem::setTileSize(const QPointF &tileSize) { - if (mTileSize != tileSize) { - mTileSize = tileSize; - emit tileSizeChanged(); - update(); - } + if (mTileSize == tileSize) + return; + + mTileSize = tileSize; + emit tileSizeChanged(); + update(); } QPointF MapGridItem::tileSize() const @@ -82,11 +83,12 @@ QPointF MapGridItem::tileSize() const void MapGridItem::setScale(const qreal &scale) { - if (mScale != scale) { - mScale = scale; - emit scaleChanged(); - update(); - } + if (mScale == scale) + return; + + mScale = scale; + emit scaleChanged(); + update(); } qreal MapGridItem::scale() const @@ -96,11 +98,12 @@ qreal MapGridItem::scale() const void MapGridItem::setColor(const QColor &color) { - if(mColor != color) { - mColor = color; - emit colorChanged(); - update(); - } + if (mColor == color) + return; + + mColor = color; + emit colorChanged(); + update(); } QColor MapGridItem::color() const diff --git a/src/libtiledquick/regionoverlay.cpp b/src/libtiledquick/regionoverlay.cpp new file mode 100644 index 0000000000..683ed95d3e --- /dev/null +++ b/src/libtiledquick/regionoverlay.cpp @@ -0,0 +1,101 @@ +/* + * regionoverlay.cpp + * Copyright 2026, UltraDagon + * + * This file is part of Tiled Quick. + * + * 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 "regionoverlay.h" + +#include +//#include "regionoverlaymaterial" + +using namespace TiledQuick; + +RegionOverlay::RegionOverlay(QQuickItem *parent) + : QQuickItem{parent} +{ + +} + +RegionOverlay::~RegionOverlay() = default; + +QSGNode *RegionOverlay::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) +{ + auto regionNode = static_cast(node); + + return regionNode; +} + +void RegionOverlay::setTileSize(const QPointF &tileSize) +{ + if (mTileSize == tileSize) + return; + + mTileSize = tileSize; + emit tileSizeChanged(); + update(); +} + +QPointF RegionOverlay::tileSize() const +{ + return mTileSize; +} + +void RegionOverlay::setScale(const qreal &scale) +{ + if (mScale == scale) + return; + + mScale = scale; + emit scaleChanged(); + update(); +} + +qreal RegionOverlay::scale() const +{ + return mScale; +} + +void RegionOverlay::setValidColor(const QColor &color) +{ + if (mValidColor == color) + return; + + mValidColor = color; + emit validColorChanged(); + update(); +} + +QColor RegionOverlay::validColor() const +{ + return mValidColor; +} + +void RegionOverlay::setInvalidColor(const QColor &color) +{ + if (mInvalidColor == color) + return; + + mInvalidColor = color; + emit invalidColorChanged(); + update(); +} + +QColor RegionOverlay::invalidColor() const +{ + return mInvalidColor; +} diff --git a/src/libtiledquick/regionoverlay.h b/src/libtiledquick/regionoverlay.h new file mode 100644 index 0000000000..09bb376322 --- /dev/null +++ b/src/libtiledquick/regionoverlay.h @@ -0,0 +1,69 @@ +/* + * regionoverlay.h + * Copyright 2026, UltraDagon + * + * This file is part of Tiled Quick. + * + * 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 + +#include "tiledquick_global.h" + +namespace TiledQuick { + +class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QPointF tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) + Q_PROPERTY(QColor validColor READ validColor WRITE setValidColor NOTIFY validColorChanged) + Q_PROPERTY(QColor invalidColor READ invalidColor WRITE setInvalidColor NOTIFY invalidColorChanged) + +public: + explicit RegionOverlay(QQuickItem *parent = nullptr); + ~RegionOverlay() override; + + QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) override; + + QPointF tileSize() const; + void setTileSize(const QPointF &tileSize); + + qreal scale() const; + void setScale(const qreal &scale); + + QColor validColor() const; + void setValidColor(const QColor &color); + + QColor invalidColor() const; + void setInvalidColor(const QColor &color); + +signals: + void tileSizeChanged(); + void scaleChanged(); + void validColorChanged(); + void invalidColorChanged(); + +private: + QPointF mTileSize = {0, 0}; + qreal mScale = 0; + QColor mValidColor = Qt::blue; + QColor mInvalidColor = Qt::red; +}; + +} // namespace TiledQuick \ No newline at end of file diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index d5e8fed987..f6f22018f9 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -87,6 +87,11 @@ Rectangle { Qt.rect(0, 0, 0, 0); } } + + Tiled.RegionOverlay { + id: regionOverlay + anchors.fill: mapItem + } } } diff --git a/src/tiledquickplugin/tiledquickplugin.cpp b/src/tiledquickplugin/tiledquickplugin.cpp index a2d6f1d590..b67764e8be 100644 --- a/src/tiledquickplugin/tiledquickplugin.cpp +++ b/src/tiledquickplugin/tiledquickplugin.cpp @@ -24,6 +24,7 @@ #include "maploader.h" #include "mapborderitem.h" #include "mapgriditem.h" +#include "regionoverlay.h" #include "tiled.h" #include @@ -38,6 +39,7 @@ void TiledQuickPlugin::registerTypes(const char *uri) qmlRegisterType(uri, 1, 0, "MapItem"); qmlRegisterType(uri, 1, 0, "MapBorderItem"); qmlRegisterType(uri, 1, 0, "MapGridItem"); + qmlRegisterType(uri, 1, 0, "RegionOverlay"); Tiled::increaseImageAllocationLimit(); } From e6ac0b9573056393b8383c9a0a80b8c5a09128dd Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Mon, 29 Jun 2026 01:22:36 -0500 Subject: [PATCH 17/29] Fixed crash that happened upon QML's garbage collection for tileEditPreview --- src/libtiled/map.cpp | 1 + src/libtiledquick/mapitem.cpp | 1 + src/tiled/mapeditor.cpp | 1 - src/tiled/resources/qml/mapview.qml | 4 +--- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libtiled/map.cpp b/src/libtiled/map.cpp index 04effaeb2f..49ee421bf4 100644 --- a/src/libtiled/map.cpp +++ b/src/libtiled/map.cpp @@ -67,6 +67,7 @@ Map::Map(Orientation orientation, Map::~Map() { qDeleteAll(mLayers); + mLayers.clear(); } /** diff --git a/src/libtiledquick/mapitem.cpp b/src/libtiledquick/mapitem.cpp index 53debd4d5b..045bcef3cc 100644 --- a/src/libtiledquick/mapitem.cpp +++ b/src/libtiledquick/mapitem.cpp @@ -30,6 +30,7 @@ using namespace TiledQuick; MapItem::MapItem(QQuickItem *parent) : QQuickItem(parent) + , mMap(nullptr) , mEditableMap(nullptr) { } diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 13195e01b4..0a385281f2 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -1138,7 +1138,6 @@ EditableMap *MapEditor::tileEditPreview() const EditableMap *editableMap = new EditableMap(atTool->brushMap().get()); - QQmlEngine::setObjectOwnership(editableMap, QQmlEngine::JavaScriptOwnership); return editableMap; } diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index f6f22018f9..a87a7b150d 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -71,10 +71,8 @@ Rectangle { anchors.left: mapItem.left anchors.top: mapItem.top - // Save toolBrushMap as a property var so that qml doesn't lose - // track of it during garbage collection property var toolPreviewMap: mapEditor.tileEditPreview - map: toolPreviewMap + map: mapEditor.tileEditPreview visibleArea: { var scale = mapContainer.scale From 5775da40e18f521aa74a4d213275ccc7c60691fa Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Mon, 29 Jun 2026 03:11:01 -0500 Subject: [PATCH 18/29] Fixed small typo in mapview.qml --- src/tiled/resources/qml/mapview.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index a87a7b150d..5b3f3a1287 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -72,7 +72,7 @@ Rectangle { anchors.top: mapItem.top property var toolPreviewMap: mapEditor.tileEditPreview - map: mapEditor.tileEditPreview + map: toolPreviewMap visibleArea: { var scale = mapContainer.scale From 26b461f1908f96918c1bea014550b106d32830fb Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:14:38 -0500 Subject: [PATCH 19/29] Fixed memory leak with setTileEditPreview; brushMapChanged now takes the map as an argument --- src/libtiled/map.cpp | 1 - src/tiled/abstracttilefilltool.cpp | 6 ++++-- src/tiled/abstracttiletool.h | 2 +- src/tiled/mapeditor.cpp | 32 +++++++++++++++++++++++------- src/tiled/mapeditor.h | 5 ++++- src/tiled/stampbrush.cpp | 4 ++-- 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/libtiled/map.cpp b/src/libtiled/map.cpp index 49ee421bf4..04effaeb2f 100644 --- a/src/libtiled/map.cpp +++ b/src/libtiled/map.cpp @@ -67,7 +67,6 @@ Map::Map(Orientation orientation, Map::~Map() { qDeleteAll(mLayers); - mLayers.clear(); } /** diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index a82a7bf310..423840d3eb 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -229,10 +229,10 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) preview->addTilesets(preview->usedTilesets()); + emit brushMapChanged(preview.get()); + brushItem()->setMap(preview); mPreviewMap = preview; - - emit brushMapChanged(); } bool AbstractTileFillTool::applyPreview(const QString &text) @@ -250,6 +250,8 @@ bool AbstractTileFillTool::applyPreview(const QString &text) void AbstractTileFillTool::clearOverlay() { + emit brushMapChanged(nullptr); + brushItem()->clear(); static_cast(brushItem())->setInvalidTiles(QRegion()); diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 46c39aa543..b23a089949 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -65,7 +65,7 @@ class AbstractTileTool : public AbstractTool void keyPressed(QKeyEvent *event) override; signals: - void brushMapChanged(); + void brushMapChanged(Map* map); protected: void mapDocumentChanged(MapDocument *oldDocument, diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 0a385281f2..ab7f451cb8 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -395,7 +395,10 @@ void MapEditor::removeDocument(Document *document) Q_ASSERT(mViewForMap.contains(mapDocument)); if (mapDocument == mCurrentMapDocument) + { setCurrentDocument(nullptr); + setTileEditPreview(nullptr); + } MapViewInterface *mapViewInterface = mViewForMap.take(mapDocument); @@ -691,6 +694,8 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) if (mSelectedTool == tool) return; + setTileEditPreview(nullptr); + if (mSelectedTool) { disconnect(mSelectedTool, &AbstractTool::cursorChanged, this, &MapEditor::cursorChanged); @@ -698,7 +703,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) AbstractTileTool *atTool = qobject_cast(mSelectedTool); if (atTool) { disconnect(atTool, &AbstractTileTool::brushMapChanged, - this, &MapEditor::tileEditPreviewChanged); + this, &MapEditor::setTileEditPreview); } #endif } @@ -723,7 +728,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) AbstractTileTool *atTool = qobject_cast(tool); if (atTool) { connect(atTool, &AbstractTileTool::brushMapChanged, - this, &MapEditor::tileEditPreviewChanged); + this, &MapEditor::setTileEditPreview); } #endif @@ -1132,13 +1137,26 @@ EditableMap *MapEditor::currentBrush() const EditableMap *MapEditor::tileEditPreview() const { - AbstractTileTool *atTool = qobject_cast(selectedTool()); - if (!atTool || !atTool->brushMap()) - return nullptr; + return mTileEditPreview.get(); +} - EditableMap *editableMap = new EditableMap(atTool->brushMap().get()); +/** +* Sets the mTileEditPreview EditableMap wrapper for the given *map. +* +* The previous *map must remain valid until after mTileEditPreview is set to +* the new *map, as EditableMap wrappers cannot be deleted if their wrapped *map +* is invalid. +*/ +void MapEditor::setTileEditPreview(Map *map) +{ + if (!map) { + mTileEditPreview.reset(); + return; + } - return editableMap; + mTileEditPreview = std::make_unique(map); + + emit tileEditPreviewChanged(); } void MapEditor::setCurrentBrush(EditableMap *editableMap) diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 38be5f5939..57500ad219 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -123,6 +123,7 @@ class MapEditor final : public Editor void setCurrentBrush(EditableMap *editableMap); EditableMap *tileEditPreview() const; + void setTileEditPreview(Map *map); EditableWangSet *currentWangSet() const; void setCurrentWangSet(EditableWangSet *wangSet); @@ -203,7 +204,7 @@ class MapEditor final : public Editor TemplatesDock *mTemplatesDock; TilesetDock *mTilesetDock; WangDock *mWangDock; - MiniMapDock* mMiniMapDock; + MiniMapDock *mMiniMapDock; TileStampsDock *mTileStampsDock; std::unique_ptr mLayerComboBox; @@ -220,6 +221,8 @@ class MapEditor final : public Editor WangBrush *mWangBrush; EditPolygonTool *mEditPolygonTool; + std::unique_ptr mTileEditPreview; + QToolBar *mMainToolBar; QToolBar *mToolsToolBar; QToolBar *mToolSpecificToolBar; diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index 55a6d223bb..ac91da1b8a 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -675,9 +675,9 @@ void StampBrush::updatePreview(QPoint tilePos) tileRegion = QRect(tilePos, tilePos); } - brushItem()->setMap(mPreviewMap, tileRegion); + emit brushMapChanged(mPreviewMap.get()); - emit brushMapChanged(); + brushItem()->setMap(mPreviewMap, tileRegion); } void StampBrush::setRandom(bool value) From fc5b3ec29c87e49c2786ff9a201e5929aad2c8b4 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:51:49 -0500 Subject: [PATCH 20/29] Removed AbstractTileTool::toolBrush(); Fixed crash caused by closing all tabs --- src/tiled/abstracttiletool.cpp | 5 ----- src/tiled/abstracttiletool.h | 2 -- src/tiled/mapeditor.cpp | 8 +++++--- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index e2059e3cb2..56c6f9714d 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -58,11 +58,6 @@ AbstractTileTool::~AbstractTileTool() delete mBrushItem; } -const SharedMap &AbstractTileTool::brushMap() const -{ - return brushItem()->map(); -} - void AbstractTileTool::activate(MapScene *scene) { scene->addItem(mBrushItem); diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index b23a089949..1e80a50574 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -52,8 +52,6 @@ class AbstractTileTool : public AbstractTool ~AbstractTileTool() override; - const SharedMap &brushMap() const; - void activate(MapScene *scene) override; void deactivate(MapScene *scene) override; diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index ab7f451cb8..6f53cbcfcc 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -425,10 +425,12 @@ void MapEditor::setCurrentDocument(Document *document) mCurrentMapDocument = mapDocument; MapViewInterface *mapViewInterface = mViewForMap.value(mapDocument); - if (mapViewInterface) - mWidgetStack->setCurrentWidget(mapViewInterface->getWidget()); + MapView *mapView = nullptr; - MapView *mapView = mapViewInterface->mapView(); + if (mapViewInterface) { + mWidgetStack->setCurrentWidget(mapViewInterface->getWidget()); + mapView = mapViewInterface->mapView(); + } mLayerDock->setMapDocument(mapDocument); From b8afcebfb140f2586eb6bd14752159a36fae05f2 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 1 Jul 2026 03:59:42 -0500 Subject: [PATCH 21/29] Added RegionOverlay for selected tiles to mapview.qml Added RegionOverlay.qml to house the qml-based drawing of the overlay Added BrushItem::regionChanged signals Added BrushItem::setRegion and BrushItem::translateRegion functions Changed BrushItem to a QGraphicsObject (from QGraphicsItem) to support signals Fixed crash caused by toggling the New Hardware Renderer preference while a tile edit preview as active Minor code style changes in mapview.qml --- src/libtiledquick/regionoverlay.cpp | 80 +++++++++++------------ src/libtiledquick/regionoverlay.h | 31 ++++----- src/tiled/abstracttiletool.cpp | 9 +++ src/tiled/abstracttiletool.h | 5 +- src/tiled/brushitem.cpp | 31 ++++++--- src/tiled/brushitem.h | 9 ++- src/tiled/mapeditor.cpp | 14 ++++ src/tiled/mapeditor.h | 4 ++ src/tiled/resources/qml/RegionOverlay.qml | 27 ++++++++ src/tiled/resources/qml/mapview.qml | 31 +++++---- 10 files changed, 155 insertions(+), 86 deletions(-) create mode 100644 src/tiled/resources/qml/RegionOverlay.qml diff --git a/src/libtiledquick/regionoverlay.cpp b/src/libtiledquick/regionoverlay.cpp index 683ed95d3e..2e27bfd975 100644 --- a/src/libtiledquick/regionoverlay.cpp +++ b/src/libtiledquick/regionoverlay.cpp @@ -20,82 +20,76 @@ #include "regionoverlay.h" -#include -//#include "regionoverlaymaterial" +#include +#include +#include using namespace TiledQuick; RegionOverlay::RegionOverlay(QQuickItem *parent) - : QQuickItem{parent} + : QQuickItem(parent) + , mTileSize(0, 0) + , mRegion(QRegion()) + , mColor(QApplication::palette().highlight().color()) { - } RegionOverlay::~RegionOverlay() = default; -QSGNode *RegionOverlay::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) +QList RegionOverlay::polygons() const { - auto regionNode = static_cast(node); + QPainterPath path; - return regionNode; -} + for (const QRect &r : mRegion.rects()) + path.addRect(r); -void RegionOverlay::setTileSize(const QPointF &tileSize) -{ - if (mTileSize == tileSize) - return; + QList polygons = path.simplified().toFillPolygons(); - mTileSize = tileSize; - emit tileSizeChanged(); - update(); + QTransform transform; + transform.scale(mTileSize.x(), mTileSize.y()); + + for (QPolygonF &polygon : polygons) + polygon = polygon * transform; + + return polygons; } -QPointF RegionOverlay::tileSize() const +QColor RegionOverlay::strokeColor() const { - return mTileSize; + return mColor; } -void RegionOverlay::setScale(const qreal &scale) +QColor RegionOverlay::fillColor() const { - if (mScale == scale) - return; - - mScale = scale; - emit scaleChanged(); - update(); + QColor fillColor = mColor; + fillColor.setAlpha(64); + return fillColor; } -qreal RegionOverlay::scale() const +QPointF RegionOverlay::tileSize() const { - return mScale; + return mTileSize; } -void RegionOverlay::setValidColor(const QColor &color) +void RegionOverlay::setTileSize(const QPointF &tileSize) { - if (mValidColor == color) + if (mTileSize == tileSize) return; - mValidColor = color; - emit validColorChanged(); - update(); + mTileSize = tileSize; + emit tileSizeChanged(); } -QColor RegionOverlay::validColor() const +QRegion RegionOverlay::region() const { - return mValidColor; + return mRegion; } -void RegionOverlay::setInvalidColor(const QColor &color) +void RegionOverlay::setRegion(const QRegion ®ion) { - if (mInvalidColor == color) + if (mRegion == region) return; - mInvalidColor = color; - emit invalidColorChanged(); - update(); -} - -QColor RegionOverlay::invalidColor() const -{ - return mInvalidColor; + mRegion = region; + emit regionChanged(); } diff --git a/src/libtiledquick/regionoverlay.h b/src/libtiledquick/regionoverlay.h index 09bb376322..a81e554d25 100644 --- a/src/libtiledquick/regionoverlay.h +++ b/src/libtiledquick/regionoverlay.h @@ -31,39 +31,30 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem Q_OBJECT Q_PROPERTY(QPointF tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) - Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) - Q_PROPERTY(QColor validColor READ validColor WRITE setValidColor NOTIFY validColorChanged) - Q_PROPERTY(QColor invalidColor READ invalidColor WRITE setInvalidColor NOTIFY invalidColorChanged) + Q_PROPERTY(QRegion region READ region WRITE setRegion NOTIFY regionChanged) public: explicit RegionOverlay(QQuickItem *parent = nullptr); ~RegionOverlay() override; - QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) override; + Q_INVOKABLE QList polygons() const; + Q_INVOKABLE QColor strokeColor() const; + Q_INVOKABLE QColor fillColor() const; QPointF tileSize() const; void setTileSize(const QPointF &tileSize); - qreal scale() const; - void setScale(const qreal &scale); - - QColor validColor() const; - void setValidColor(const QColor &color); - - QColor invalidColor() const; - void setInvalidColor(const QColor &color); + QRegion region() const; + void setRegion(const QRegion ®ion); signals: void tileSizeChanged(); - void scaleChanged(); - void validColorChanged(); - void invalidColorChanged(); + void regionChanged(); private: - QPointF mTileSize = {0, 0}; - qreal mScale = 0; - QColor mValidColor = Qt::blue; - QColor mInvalidColor = Qt::red; + QPointF mTileSize; + QRegion mRegion; + QColor mColor; }; -} // namespace TiledQuick \ No newline at end of file +} // namespace TiledQuick diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 56c6f9714d..720cdf67da 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -51,13 +51,22 @@ AbstractTileTool::AbstractTileTool(Id id, mBrushItem = new BrushItem; mBrushItem->setVisible(false); mBrushItem->setZValue(10000); + connect(mBrushItem, &BrushItem::regionChanged, + this, &AbstractTileTool::brushRegionChanged); } AbstractTileTool::~AbstractTileTool() { + disconnect(mBrushItem, &BrushItem::regionChanged, + this, &AbstractTileTool::brushRegionChanged); delete mBrushItem; } +QRegion AbstractTileTool::validRegion() const +{ + return mBrushItem->tileRegion(); +} + void AbstractTileTool::activate(MapScene *scene) { scene->addItem(mBrushItem); diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 1e80a50574..f99f38e531 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -52,6 +52,8 @@ class AbstractTileTool : public AbstractTool ~AbstractTileTool() override; + QRegion validRegion() const; + void activate(MapScene *scene) override; void deactivate(MapScene *scene) override; @@ -63,7 +65,8 @@ class AbstractTileTool : public AbstractTool void keyPressed(QKeyEvent *event) override; signals: - void brushMapChanged(Map* map); + void brushMapChanged(Tiled::Map *map); + void brushRegionChanged(); protected: void mapDocumentChanged(MapDocument *oldDocument, diff --git a/src/tiled/brushitem.cpp b/src/tiled/brushitem.cpp index 23056b6d31..253c09994f 100644 --- a/src/tiled/brushitem.cpp +++ b/src/tiled/brushitem.cpp @@ -61,7 +61,7 @@ void BrushItem::clear() { mTileLayer.clear(); mMap.clear(); - mRegion = QRegion(); + setRegion(QRegion()); updateBoundingRect(); update(); @@ -86,7 +86,7 @@ void BrushItem::setTileLayer(const SharedTileLayer &tileLayer, const QRegion ®ion) { mTileLayer = tileLayer; - mRegion = region; + setRegion(region); updateBoundingRect(); update(); @@ -100,7 +100,7 @@ void BrushItem::setMap(const SharedMap &map) void BrushItem::setMap(const SharedMap &map, const QRegion ®ion) { mMap = map; - mRegion = region; + setRegion(region); updateBoundingRect(); update(); @@ -118,7 +118,7 @@ void BrushItem::setTileLayerPosition(QPoint pos) if (oldPosition == pos) return; - mRegion.translate(pos - oldPosition); + translateRegion(pos - oldPosition); mTileLayer->setX(pos.x()); mTileLayer->setY(pos.y()); updateBoundingRect(); @@ -129,10 +129,7 @@ void BrushItem::setTileLayerPosition(QPoint pos) */ void BrushItem::setTileRegion(const QRegion ®ion) { - if (mRegion == region) - return; - - mRegion = region; + setRegion(region); updateBoundingRect(); } @@ -236,3 +233,21 @@ void BrushItem::updateBoundingRect() // Adjust for border drawn at tile selection edges mBoundingRect.adjust(-1, -1, 1, 1); } + +void BrushItem::setRegion(const QRegion ®ion) +{ + if (mRegion == region) + return; + + mRegion = region; + emit regionChanged(); +} + +void BrushItem::translateRegion(const QPoint &p) +{ + if (p.x() == 0 && p.y() == 0) + return; + + mRegion.translate(p); + emit regionChanged(); +} diff --git a/src/tiled/brushitem.h b/src/tiled/brushitem.h index 8c313cad2f..84f868a76f 100644 --- a/src/tiled/brushitem.h +++ b/src/tiled/brushitem.h @@ -34,8 +34,10 @@ class MapDocument; * This brush item is used to represent a brush in a map scene before it is * used. */ -class BrushItem : public QGraphicsItem +class BrushItem : public QGraphicsObject { + Q_OBJECT + public: BrushItem(); @@ -64,11 +66,16 @@ class BrushItem : public QGraphicsItem const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; +signals: + void regionChanged(); + protected: MapDocument *mapDocument() const { return mMapDocument; } private: void updateBoundingRect(); + void setRegion(const QRegion ®ion); + void translateRegion(const QPoint &p); MapDocument *mMapDocument; SharedTileLayer mTileLayer; diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 6f53cbcfcc..dead2cd9b7 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -706,6 +706,8 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) if (atTool) { disconnect(atTool, &AbstractTileTool::brushMapChanged, this, &MapEditor::setTileEditPreview); + disconnect(atTool, &AbstractTileTool::brushRegionChanged, + this, &MapEditor::tileEditRegionChanged); } #endif } @@ -731,6 +733,8 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) if (atTool) { connect(atTool, &AbstractTileTool::brushMapChanged, this, &MapEditor::setTileEditPreview); + connect(atTool, &AbstractTileTool::brushRegionChanged, + this, &MapEditor::tileEditRegionChanged); } #endif @@ -1082,6 +1086,8 @@ void MapEditor::setUseOpenGL(bool useOpenGL) #ifdef TILEDQUICK_LIB void MapEditor::setUseNewHardwareRenderer(bool useNewHardwareRenderer) { + setTileEditPreview(nullptr); + for (MapViewInterface *mapViewInterface : std::as_const(mViewForMap)) { mWidgetStack->addWidget(mapViewInterface->getWidget()); @@ -1161,6 +1167,14 @@ void MapEditor::setTileEditPreview(Map *map) emit tileEditPreviewChanged(); } +QRegion MapEditor::tileEditRegion() const +{ + AbstractTileTool *atTool = qobject_cast(selectedTool()); + if (atTool) + return atTool->validRegion(); + return QRegion(); +} + void MapEditor::setCurrentBrush(EditableMap *editableMap) { if (!editableMap) { diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 57500ad219..a9c14efde2 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -76,6 +76,7 @@ class MapEditor final : public Editor Q_PROPERTY(Tiled::TilesetDock *tilesetsView READ tilesetDock CONSTANT) Q_PROPERTY(Tiled::EditableMap *currentBrush READ currentBrush WRITE setCurrentBrush NOTIFY currentBrushChanged) Q_PROPERTY(Tiled::EditableMap *tileEditPreview READ tileEditPreview NOTIFY tileEditPreviewChanged) + Q_PROPERTY(QRegion tileEditRegion READ tileEditRegion NOTIFY tileEditRegionChanged) Q_PROPERTY(Tiled::EditableWangSet *currentWangSet READ currentWangSet WRITE setCurrentWangSet NOTIFY currentWangSetChanged) Q_PROPERTY(int currentWangColorIndex READ currentWangColorIndex WRITE setCurrentWangColorIndex NOTIFY currentWangColorIndexChanged) Q_PROPERTY(Tiled::MapView *currentMapView READ currentMapView CONSTANT) @@ -125,6 +126,8 @@ class MapEditor final : public Editor EditableMap *tileEditPreview() const; void setTileEditPreview(Map *map); + QRegion tileEditRegion() const; + EditableWangSet *currentWangSet() const; void setCurrentWangSet(EditableWangSet *wangSet); @@ -148,6 +151,7 @@ class MapEditor final : public Editor signals: void currentBrushChanged(); void tileEditPreviewChanged(); + void tileEditRegionChanged(); void currentWangSetChanged(); void currentWangColorIndexChanged(int colorIndex); void selectedToolChanged(AbstractTool *tool); diff --git a/src/tiled/resources/qml/RegionOverlay.qml b/src/tiled/resources/qml/RegionOverlay.qml new file mode 100644 index 0000000000..fc3e3fa5f0 --- /dev/null +++ b/src/tiled/resources/qml/RegionOverlay.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Shapes +import QtQuick.Controls +import org.mapeditor.Tiled as Tiled + +Tiled.RegionOverlay { + id: regionOverlay + + property var scale + property var regionPolygons + + onRegionChanged: { + regionOverlay.regionPolygons = regionOverlay.polygons() + } + + Shape { + ShapePath { + strokeColor: regionOverlay.strokeColor() + strokeWidth: 1 / regionOverlay.scale + fillColor: regionOverlay.fillColor() + + PathMultiline { + paths: regionOverlay.regionPolygons + } + } + } +} diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index 5b3f3a1287..eda02a7921 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -38,14 +38,14 @@ Rectangle { Tiled.MapItem { id: mapItem - map: mapItemMap; + map: mapItemMap visibleArea: { var scale = mapContainer.scale Qt.rect(-mapContainer.x / scale, -mapContainer.y / scale, mapView.width / scale, - mapView.height / scale); + mapView.height / scale) } } @@ -60,8 +60,8 @@ Rectangle { id: mapGridItem anchors.fill: mapItem - tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight); - scale: mapContainer.scale; + tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight) + scale: mapContainer.scale color: "black" } @@ -75,20 +75,25 @@ Rectangle { map: toolPreviewMap visibleArea: { - var scale = mapContainer.scale + // TODO: Adjust to only show needed visible area if (this.map) - Qt.rect(-mapContainer.x / scale, - -mapContainer.y / scale, - mapView.width / scale, - mapView.height / scale); + Qt.rect(0, + 0, + this.width, + this.height) else - Qt.rect(0, 0, 0, 0); + Qt.rect(0, 0, 0, 0) } } - Tiled.RegionOverlay { - id: regionOverlay + RegionOverlay { + id: validRegionOverlay anchors.fill: mapItem + + scale: mapContainer.scale + + region: mapEditor.tileEditRegion + tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight) } } } @@ -159,7 +164,7 @@ Rectangle { } function cursorTileCoords() { - var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y); + var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y) return tileCoords } From 163e803dde131f489963b606a23483268aa787e8 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:10:48 -0500 Subject: [PATCH 22/29] Linked quick mouse onPress and onRelease events to selectedTool --- src/tiled/abstracttiletool.cpp | 2 +- src/tiled/mapeditor.cpp | 32 ++++++++++++++++++++++++++++ src/tiled/mapeditor.h | 2 ++ src/tiled/resources/qml/DragArea.qml | 1 + src/tiled/resources/qml/mapview.qml | 18 ++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 720cdf67da..1a0c3bbb7f 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -252,7 +252,7 @@ void AbstractTileTool::updateBrushVisibility() } } - mBrushItem->setVisible(showBrush); + mBrushItem->setVisible(showBrush || Preferences::instance()->useNewHardwareRenderer()); } /** diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index dead2cd9b7..02ade8e927 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -1230,6 +1230,38 @@ void MapEditor::setQuickMouseCoords(QPointF coords) selectedTool()->mouseMoved(coords, Qt::NoModifier); } + +void MapEditor::quickMousePressed(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos) +{ + if (!selectedTool()) + return; + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.setButton(button); + event.setButtons(buttons); + event.setModifiers(modifiers); + event.setPos(pos); + event.setScenePos(scenePos); + event.setScreenPos(screenPos); + + selectedTool()->mousePressed(&event); +} + +void MapEditor::quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos) +{ + if (!selectedTool()) + return; + + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.setButton(button); + event.setButtons(buttons); + event.setModifiers(modifiers); + event.setPos(pos); + event.setScenePos(scenePos); + event.setScreenPos(screenPos); + + selectedTool()->mouseReleased(&event); +} #endif AbstractTool *MapEditor::tool(const QByteArray &id) const diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index a9c14efde2..60515e3e50 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -144,6 +144,8 @@ class MapEditor final : public Editor #ifdef TILEDQUICK_LIB Q_INVOKABLE void setQuickMouseCoords(QPointF coords); + Q_INVOKABLE void quickMousePressed(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); + Q_INVOKABLE void quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); #endif Q_INVOKABLE AbstractTool *tool(const QByteArray &id) const; diff --git a/src/tiled/resources/qml/DragArea.qml b/src/tiled/resources/qml/DragArea.qml index bcb0cff525..76107dd56c 100644 --- a/src/tiled/resources/qml/DragArea.qml +++ b/src/tiled/resources/qml/DragArea.qml @@ -5,6 +5,7 @@ MouseArea { signal dragged(var dx, var dy) + acceptedButtons: Qt.AllButtons hoverEnabled: true onPressed: function(mouse) { lastDragPos = mapToItem(null, mouse.x, mouse.y) diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index eda02a7921..cef6a27880 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -138,6 +138,24 @@ Rectangle { containerAnimation.scale = targetScale containerAnimation.start() } + + onPressed: (event) => mapEditor.quickMousePressed( + event.button, + event.buttons, + event.modifiers, + singleFingerPanArea.mapToItem(mapItem, event.x, event.y), + singleFingerPanArea.mapToItem(null, event.x, event.y), + singleFingerPanArea.mapToGlobal(event.x, event.y) + ) + + onReleased: (event) => mapEditor.quickMouseReleased( + event.button, + event.buttons, + event.modifiers, + singleFingerPanArea.mapToItem(mapItem, event.x, event.y), + singleFingerPanArea.mapToItem(null, event.x, event.y), + singleFingerPanArea.mapToGlobal(event.x, event.y) + ) } function fitMapInView(animate = true) { From 40b17760954ef4a88fda8df3e42ee2164b1ddec4 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:08:16 -0500 Subject: [PATCH 23/29] Moved tileEditRegionChanged code from brushItem to abstractTileTool & children --- src/tiled/abstracttilefilltool.cpp | 1 + src/tiled/abstracttiletool.cpp | 4 ---- src/tiled/abstracttiletool.h | 2 +- src/tiled/brushitem.cpp | 2 -- src/tiled/brushitem.h | 7 +------ src/tiled/mapeditor.cpp | 16 ++++++++++------ src/tiled/mapeditor.h | 2 ++ src/tiled/resources/qml/DragArea.qml | 2 +- src/tiled/stampbrush.cpp | 5 +++++ 9 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index 423840d3eb..c7bd4285c2 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -230,6 +230,7 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) preview->addTilesets(preview->usedTilesets()); emit brushMapChanged(preview.get()); + emit brushRegionChanged(fillRegion); brushItem()->setMap(preview); mPreviewMap = preview; diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 1a0c3bbb7f..1ddb730b3b 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -51,14 +51,10 @@ AbstractTileTool::AbstractTileTool(Id id, mBrushItem = new BrushItem; mBrushItem->setVisible(false); mBrushItem->setZValue(10000); - connect(mBrushItem, &BrushItem::regionChanged, - this, &AbstractTileTool::brushRegionChanged); } AbstractTileTool::~AbstractTileTool() { - disconnect(mBrushItem, &BrushItem::regionChanged, - this, &AbstractTileTool::brushRegionChanged); delete mBrushItem; } diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index f99f38e531..3d01650880 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -66,7 +66,7 @@ class AbstractTileTool : public AbstractTool signals: void brushMapChanged(Tiled::Map *map); - void brushRegionChanged(); + void brushRegionChanged(const QRegion ®ion); protected: void mapDocumentChanged(MapDocument *oldDocument, diff --git a/src/tiled/brushitem.cpp b/src/tiled/brushitem.cpp index 253c09994f..901798ec87 100644 --- a/src/tiled/brushitem.cpp +++ b/src/tiled/brushitem.cpp @@ -240,7 +240,6 @@ void BrushItem::setRegion(const QRegion ®ion) return; mRegion = region; - emit regionChanged(); } void BrushItem::translateRegion(const QPoint &p) @@ -249,5 +248,4 @@ void BrushItem::translateRegion(const QPoint &p) return; mRegion.translate(p); - emit regionChanged(); } diff --git a/src/tiled/brushitem.h b/src/tiled/brushitem.h index 84f868a76f..3f2028c290 100644 --- a/src/tiled/brushitem.h +++ b/src/tiled/brushitem.h @@ -34,10 +34,8 @@ class MapDocument; * This brush item is used to represent a brush in a map scene before it is * used. */ -class BrushItem : public QGraphicsObject +class BrushItem : public QGraphicsItem { - Q_OBJECT - public: BrushItem(); @@ -66,9 +64,6 @@ class BrushItem : public QGraphicsObject const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; -signals: - void regionChanged(); - protected: MapDocument *mapDocument() const { return mMapDocument; } diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 02ade8e927..b572503394 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -707,7 +707,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) disconnect(atTool, &AbstractTileTool::brushMapChanged, this, &MapEditor::setTileEditPreview); disconnect(atTool, &AbstractTileTool::brushRegionChanged, - this, &MapEditor::tileEditRegionChanged); + this, &MapEditor::setTileEditRegion); } #endif } @@ -734,7 +734,7 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) connect(atTool, &AbstractTileTool::brushMapChanged, this, &MapEditor::setTileEditPreview); connect(atTool, &AbstractTileTool::brushRegionChanged, - this, &MapEditor::tileEditRegionChanged); + this, &MapEditor::setTileEditRegion); } #endif @@ -1169,10 +1169,14 @@ void MapEditor::setTileEditPreview(Map *map) QRegion MapEditor::tileEditRegion() const { - AbstractTileTool *atTool = qobject_cast(selectedTool()); - if (atTool) - return atTool->validRegion(); - return QRegion(); + return mTileEditRegion; +} + +void MapEditor::setTileEditRegion(const QRegion ®ion) +{ + mTileEditRegion = region; + + emit tileEditRegionChanged(); } void MapEditor::setCurrentBrush(EditableMap *editableMap) diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 60515e3e50..1e57c3d387 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -127,6 +127,7 @@ class MapEditor final : public Editor void setTileEditPreview(Map *map); QRegion tileEditRegion() const; + void setTileEditRegion(const QRegion ®ion); EditableWangSet *currentWangSet() const; void setCurrentWangSet(EditableWangSet *wangSet); @@ -228,6 +229,7 @@ class MapEditor final : public Editor EditPolygonTool *mEditPolygonTool; std::unique_ptr mTileEditPreview; + QRegion mTileEditRegion; QToolBar *mMainToolBar; QToolBar *mToolsToolBar; diff --git a/src/tiled/resources/qml/DragArea.qml b/src/tiled/resources/qml/DragArea.qml index 76107dd56c..b40cd2fb0e 100644 --- a/src/tiled/resources/qml/DragArea.qml +++ b/src/tiled/resources/qml/DragArea.qml @@ -12,7 +12,7 @@ MouseArea { } onPositionChanged: function(mouse) { - if (!pressed) + if (!(pressedButtons & Qt.MiddleButton)) return; var pos = mapToItem(null, mouse.x, mouse.y) diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index ac91da1b8a..d67587a25a 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -96,7 +96,10 @@ void StampBrush::tilePositionChanged(QPoint pos) // Only update the brush item for the last drawn piece if (i == points.size() - 1) + { + emit brushMapChanged(mPreviewMap.get()); brushItem()->setMap(mPreviewMap); + } doPaint(Mergeable, &paintedRegions); } @@ -104,6 +107,7 @@ void StampBrush::tilePositionChanged(QPoint pos) QHashIterator ri(paintedRegions); while (ri.hasNext()) { ri.next(); + emit brushRegionChanged(ri.value()); emit mapDocument()->regionEdited(ri.value(), ri.key()); } } else { @@ -676,6 +680,7 @@ void StampBrush::updatePreview(QPoint tilePos) } emit brushMapChanged(mPreviewMap.get()); + emit brushRegionChanged(tileRegion); brushItem()->setMap(mPreviewMap, tileRegion); } From 5d21d771e896e6facb522d9528fd3a9585c25c98 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Sat, 4 Jul 2026 19:02:16 -0500 Subject: [PATCH 24/29] Changed brushRegionOverlay to indicate if brush is in or out of map bounds Changed brushRegionOverlay to be visible/invisible when mouse is in/out of view Added validPolygons and invalidPolygons Q_PROPERTYs to RegionOverlay Changed StampBrush to hide tileEditPreview map upon drag Changed StampBrush's brush drag brushRegionChanged to be out of a for loop Added brushRegionChanged emits to AbstractTileSelectionTool and Eraser Removed unused AbstractTileTool::validRegion() getter --- src/libtiledquick/regionoverlay.cpp | 93 ++++++++++++++++++----- src/libtiledquick/regionoverlay.h | 22 +++++- src/tiled/abstracttileselectiontool.cpp | 1 + src/tiled/abstracttiletool.cpp | 7 +- src/tiled/abstracttiletool.h | 2 - src/tiled/eraser.cpp | 1 + src/tiled/mapeditor.cpp | 12 +++ src/tiled/mapeditor.h | 1 + src/tiled/resources/qml/RegionOverlay.qml | 21 +++-- src/tiled/resources/qml/mapview.qml | 10 ++- src/tiled/stampbrush.cpp | 8 +- 11 files changed, 133 insertions(+), 45 deletions(-) diff --git a/src/libtiledquick/regionoverlay.cpp b/src/libtiledquick/regionoverlay.cpp index 2e27bfd975..dafe6b88e8 100644 --- a/src/libtiledquick/regionoverlay.cpp +++ b/src/libtiledquick/regionoverlay.cpp @@ -30,38 +30,34 @@ RegionOverlay::RegionOverlay(QQuickItem *parent) : QQuickItem(parent) , mTileSize(0, 0) , mRegion(QRegion()) - , mColor(QApplication::palette().highlight().color()) + , mMapRect(QRect()) + , mValidColor(QApplication::palette().highlight().color()) + , mInvalidColor(QColor(255,0,0)) { } RegionOverlay::~RegionOverlay() = default; -QList RegionOverlay::polygons() const +QColor RegionOverlay::validStrokeColor() const { - QPainterPath path; - - for (const QRect &r : mRegion.rects()) - path.addRect(r); - - QList polygons = path.simplified().toFillPolygons(); - - QTransform transform; - transform.scale(mTileSize.x(), mTileSize.y()); - - for (QPolygonF &polygon : polygons) - polygon = polygon * transform; + return mValidColor; +} - return polygons; +QColor RegionOverlay::validFillColor() const +{ + QColor fillColor = mValidColor; + fillColor.setAlpha(64); + return fillColor; } -QColor RegionOverlay::strokeColor() const +QColor RegionOverlay::invalidStrokeColor() const { - return mColor; + return mInvalidColor; } -QColor RegionOverlay::fillColor() const +QColor RegionOverlay::invalidFillColor() const { - QColor fillColor = mColor; + QColor fillColor = mInvalidColor; fillColor.setAlpha(64); return fillColor; } @@ -93,3 +89,62 @@ void RegionOverlay::setRegion(const QRegion ®ion) mRegion = region; emit regionChanged(); } + +QRect RegionOverlay::mapRect() const +{ + return mMapRect; +} + +void RegionOverlay::setMapRect(const QRect &rect) +{ + if (mMapRect == rect) + return; + + mMapRect = rect; + emit mapRectChanged(); +} + +QList RegionOverlay::validPolygons() const +{ + QRegion insideMapRegion; + if (mMapRect == QRect()) + insideMapRegion = mRegion; + else + insideMapRegion = mRegion.intersected(mMapRect); + + QPainterPath path; + for (const QRect &r : insideMapRegion.rects()) + path.addRect(r); + + QList polygons = path.simplified().toSubpathPolygons(); + + QTransform transform; + transform.scale(mTileSize.x(), mTileSize.y()); + + for (QPolygonF &polygon : polygons) + polygon = transform.map(polygon); + + return polygons; +} + +QList RegionOverlay::invalidPolygons() const +{ + if (mMapRect == QRect()) + return QList(); + + QRegion outsideMapRegion = mRegion.subtracted(mMapRect); + + QPainterPath path; + for (const QRect &r : outsideMapRegion.rects()) + path.addRect(r); + + QList polygons = path.simplified().toSubpathPolygons(); + + QTransform transform; + transform.scale(mTileSize.x(), mTileSize.y()); + + for (QPolygonF &polygon : polygons) + polygon = transform.map(polygon); + + return polygons; +} diff --git a/src/libtiledquick/regionoverlay.h b/src/libtiledquick/regionoverlay.h index a81e554d25..58965d7df0 100644 --- a/src/libtiledquick/regionoverlay.h +++ b/src/libtiledquick/regionoverlay.h @@ -32,14 +32,19 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem Q_PROPERTY(QPointF tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) Q_PROPERTY(QRegion region READ region WRITE setRegion NOTIFY regionChanged) + Q_PROPERTY(QRect mapRect READ mapRect WRITE setMapRect NOTIFY mapRectChanged) + Q_PROPERTY(QList validPolygons READ validPolygons NOTIFY regionChanged) + Q_PROPERTY(QList invalidPolygons READ invalidPolygons NOTIFY regionChanged) public: explicit RegionOverlay(QQuickItem *parent = nullptr); ~RegionOverlay() override; - Q_INVOKABLE QList polygons() const; - Q_INVOKABLE QColor strokeColor() const; - Q_INVOKABLE QColor fillColor() const; + Q_INVOKABLE QColor validStrokeColor() const; + Q_INVOKABLE QColor validFillColor() const; + + Q_INVOKABLE QColor invalidStrokeColor() const; + Q_INVOKABLE QColor invalidFillColor() const; QPointF tileSize() const; void setTileSize(const QPointF &tileSize); @@ -47,14 +52,23 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem QRegion region() const; void setRegion(const QRegion ®ion); + QRect mapRect() const; + void setMapRect(const QRect &rect); + + QList validPolygons() const; + QList invalidPolygons() const; + signals: void tileSizeChanged(); void regionChanged(); + void mapRectChanged(); private: QPointF mTileSize; QRegion mRegion; - QColor mColor; + QRect mMapRect; + QColor mValidColor; + QColor mInvalidColor; }; } // namespace TiledQuick diff --git a/src/tiled/abstracttileselectiontool.cpp b/src/tiled/abstracttileselectiontool.cpp index 895f8c1027..431abd2722 100644 --- a/src/tiled/abstracttileselectiontool.cpp +++ b/src/tiled/abstracttileselectiontool.cpp @@ -176,6 +176,7 @@ const QRegion &AbstractTileSelectionTool::selectionPreviewRegion() const void AbstractTileSelectionTool::setSelectionPreview(const QRegion ®ion) { brushItem()->setTileRegion(region); + emit brushRegionChanged(region); } void AbstractTileSelectionTool::applySelectionPreview() diff --git a/src/tiled/abstracttiletool.cpp b/src/tiled/abstracttiletool.cpp index 1ddb730b3b..56c6f9714d 100644 --- a/src/tiled/abstracttiletool.cpp +++ b/src/tiled/abstracttiletool.cpp @@ -58,11 +58,6 @@ AbstractTileTool::~AbstractTileTool() delete mBrushItem; } -QRegion AbstractTileTool::validRegion() const -{ - return mBrushItem->tileRegion(); -} - void AbstractTileTool::activate(MapScene *scene) { scene->addItem(mBrushItem); @@ -248,7 +243,7 @@ void AbstractTileTool::updateBrushVisibility() } } - mBrushItem->setVisible(showBrush || Preferences::instance()->useNewHardwareRenderer()); + mBrushItem->setVisible(showBrush); } /** diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 3d01650880..14b8b04e9a 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -52,8 +52,6 @@ class AbstractTileTool : public AbstractTool ~AbstractTileTool() override; - QRegion validRegion() const; - void activate(MapScene *scene) override; void deactivate(MapScene *scene) override; diff --git a/src/tiled/eraser.cpp b/src/tiled/eraser.cpp index b97af48d5f..ad314cd753 100644 --- a/src/tiled/eraser.cpp +++ b/src/tiled/eraser.cpp @@ -46,6 +46,7 @@ void Eraser::tilePositionChanged(QPoint tilePos) Q_UNUSED(tilePos) brushItem()->setTileRegion(eraseArea()); + emit brushRegionChanged(eraseArea()); if (mMode == Erase) doErase(true); diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index b572503394..9b1f246ac4 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -1266,6 +1266,18 @@ void MapEditor::quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons butt selectedTool()->mouseReleased(&event); } + +void MapEditor::quickContainsMouseChanged(bool viewContainsMouse) +{ + if (!selectedTool()) + return; + + if (viewContainsMouse) { + selectedTool()->mouseEntered(); + } else { + selectedTool()->mouseLeft(); + } +} #endif AbstractTool *MapEditor::tool(const QByteArray &id) const diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 1e57c3d387..1fc7cd5c33 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -147,6 +147,7 @@ class MapEditor final : public Editor Q_INVOKABLE void setQuickMouseCoords(QPointF coords); Q_INVOKABLE void quickMousePressed(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); Q_INVOKABLE void quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); + Q_INVOKABLE void quickContainsMouseChanged(bool viewContainsMouse); #endif Q_INVOKABLE AbstractTool *tool(const QByteArray &id) const; diff --git a/src/tiled/resources/qml/RegionOverlay.qml b/src/tiled/resources/qml/RegionOverlay.qml index fc3e3fa5f0..af6b237d08 100644 --- a/src/tiled/resources/qml/RegionOverlay.qml +++ b/src/tiled/resources/qml/RegionOverlay.qml @@ -7,20 +7,25 @@ Tiled.RegionOverlay { id: regionOverlay property var scale - property var regionPolygons - - onRegionChanged: { - regionOverlay.regionPolygons = regionOverlay.polygons() - } Shape { ShapePath { - strokeColor: regionOverlay.strokeColor() + strokeColor: regionOverlay.validStrokeColor() + strokeWidth: 1 / regionOverlay.scale + fillColor: regionOverlay.validFillColor() + + PathMultiline { + paths: regionOverlay.validPolygons + } + } + + ShapePath { + strokeColor: regionOverlay.invalidStrokeColor() strokeWidth: 1 / regionOverlay.scale - fillColor: regionOverlay.fillColor() + fillColor: regionOverlay.invalidFillColor() PathMultiline { - paths: regionOverlay.regionPolygons + paths: regionOverlay.invalidPolygons } } } diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index cef6a27880..d53da2a493 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -49,6 +49,8 @@ Rectangle { } } + // TODO: Need RegionOverlay for selected tiles from tile selection tools. + Tiled.MapBorderItem { id: mapBorderItem anchors.fill: mapItem @@ -70,6 +72,7 @@ Rectangle { id: toolBrush anchors.left: mapItem.left anchors.top: mapItem.top + visible: singleFingerPanArea.containsMouse property var toolPreviewMap: mapEditor.tileEditPreview map: toolPreviewMap @@ -87,12 +90,13 @@ Rectangle { } RegionOverlay { - id: validRegionOverlay + id: brushRegionOverlay anchors.fill: mapItem + visible: singleFingerPanArea.containsMouse scale: mapContainer.scale - region: mapEditor.tileEditRegion + mapRect: Qt.rect(0, 0, mapItem.map.width, mapItem.map.height) tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight) } } @@ -156,6 +160,8 @@ Rectangle { singleFingerPanArea.mapToItem(null, event.x, event.y), singleFingerPanArea.mapToGlobal(event.x, event.y) ) + + onContainsMouseChanged: mapEditor.quickContainsMouseChanged(singleFingerPanArea.containsMouse) } function fitMapInView(animate = true) { diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index d67587a25a..ea40194c1e 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -91,15 +91,14 @@ void StampBrush::tilePositionChanged(QPoint pos) QVector points = pointsOnLine(mPrevTilePosition, pos); QHash paintedRegions; + emit brushMapChanged(nullptr); + for (int i = 1; i < points.size(); ++i) { drawPreviewLayer(QVector() << points.at(i)); // Only update the brush item for the last drawn piece if (i == points.size() - 1) - { - emit brushMapChanged(mPreviewMap.get()); brushItem()->setMap(mPreviewMap); - } doPaint(Mergeable, &paintedRegions); } @@ -107,9 +106,10 @@ void StampBrush::tilePositionChanged(QPoint pos) QHashIterator ri(paintedRegions); while (ri.hasNext()) { ri.next(); - emit brushRegionChanged(ri.value()); emit mapDocument()->regionEdited(ri.value(), ri.key()); } + + emit brushRegionChanged(mPreviewMap->modifiedTileRegion()); } else { updatePreview(); } From d4a3f4a40a85557ecc761a8a5a01fe582c8a4422 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Mon, 6 Jul 2026 22:43:44 -0500 Subject: [PATCH 25/29] Added selectedRegionOverlay; RegionOverlay tweaks Added regionAlpha Q_PROPERTY to RegionOverlay Moved RegionOverlay's duplicate polygons code to it's own private function Changed toolBrush/regionOverlay to stay visible while mouse is pressed --- src/libtiledquick/regionoverlay.cpp | 40 +++++++++++++++++------------ src/libtiledquick/regionoverlay.h | 8 ++++++ src/tiled/mapeditor.cpp | 10 ++++++++ src/tiled/mapeditor.h | 4 +++ src/tiled/resources/qml/mapview.qml | 15 ++++++++--- 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/libtiledquick/regionoverlay.cpp b/src/libtiledquick/regionoverlay.cpp index dafe6b88e8..8f6d40f648 100644 --- a/src/libtiledquick/regionoverlay.cpp +++ b/src/libtiledquick/regionoverlay.cpp @@ -33,6 +33,7 @@ RegionOverlay::RegionOverlay(QQuickItem *parent) , mMapRect(QRect()) , mValidColor(QApplication::palette().highlight().color()) , mInvalidColor(QColor(255,0,0)) + , mRegionAlpha(64) { } @@ -46,7 +47,7 @@ QColor RegionOverlay::validStrokeColor() const QColor RegionOverlay::validFillColor() const { QColor fillColor = mValidColor; - fillColor.setAlpha(64); + fillColor.setAlpha(mRegionAlpha); return fillColor; } @@ -58,7 +59,7 @@ QColor RegionOverlay::invalidStrokeColor() const QColor RegionOverlay::invalidFillColor() const { QColor fillColor = mInvalidColor; - fillColor.setAlpha(64); + fillColor.setAlpha(mRegionAlpha); return fillColor; } @@ -104,6 +105,20 @@ void RegionOverlay::setMapRect(const QRect &rect) emit mapRectChanged(); } +int RegionOverlay::regionAlpha() const +{ + return mRegionAlpha; +} + +void RegionOverlay::setRegionAlpha(const int &alpha) +{ + if (mRegionAlpha == alpha) + return; + + mRegionAlpha = alpha; + emit regionAlphaChanged(); +} + QList RegionOverlay::validPolygons() const { QRegion insideMapRegion; @@ -112,19 +127,7 @@ QList RegionOverlay::validPolygons() const else insideMapRegion = mRegion.intersected(mMapRect); - QPainterPath path; - for (const QRect &r : insideMapRegion.rects()) - path.addRect(r); - - QList polygons = path.simplified().toSubpathPolygons(); - - QTransform transform; - transform.scale(mTileSize.x(), mTileSize.y()); - - for (QPolygonF &polygon : polygons) - polygon = transform.map(polygon); - - return polygons; + return polygons(insideMapRegion); } QList RegionOverlay::invalidPolygons() const @@ -134,8 +137,13 @@ QList RegionOverlay::invalidPolygons() const QRegion outsideMapRegion = mRegion.subtracted(mMapRect); + return polygons(outsideMapRegion); +} + +QList RegionOverlay::polygons(const QRegion ®ion) const +{ QPainterPath path; - for (const QRect &r : outsideMapRegion.rects()) + for (const QRect &r : region.rects()) path.addRect(r); QList polygons = path.simplified().toSubpathPolygons(); diff --git a/src/libtiledquick/regionoverlay.h b/src/libtiledquick/regionoverlay.h index 58965d7df0..e1f0999920 100644 --- a/src/libtiledquick/regionoverlay.h +++ b/src/libtiledquick/regionoverlay.h @@ -33,6 +33,7 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem Q_PROPERTY(QPointF tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) Q_PROPERTY(QRegion region READ region WRITE setRegion NOTIFY regionChanged) Q_PROPERTY(QRect mapRect READ mapRect WRITE setMapRect NOTIFY mapRectChanged) + Q_PROPERTY(int regionAlpha READ regionAlpha WRITE setRegionAlpha NOTIFY regionAlphaChanged) Q_PROPERTY(QList validPolygons READ validPolygons NOTIFY regionChanged) Q_PROPERTY(QList invalidPolygons READ invalidPolygons NOTIFY regionChanged) @@ -55,6 +56,9 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem QRect mapRect() const; void setMapRect(const QRect &rect); + int regionAlpha() const; + void setRegionAlpha(const int &alpha); + QList validPolygons() const; QList invalidPolygons() const; @@ -62,13 +66,17 @@ class TILEDQUICK_SHARED_EXPORT RegionOverlay : public QQuickItem void tileSizeChanged(); void regionChanged(); void mapRectChanged(); + void regionAlphaChanged(); private: + QList polygons(const QRegion ®ion) const; + QPointF mTileSize; QRegion mRegion; QRect mMapRect; QColor mValidColor; QColor mInvalidColor; + int mRegionAlpha; }; } // namespace TiledQuick diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 9b1f246ac4..74a32b9a41 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -453,6 +453,8 @@ void MapEditor::setCurrentDocument(Document *document) // this, &MapEditor::updateActions); // connect(mapDocument, &MapDocument::selectedObjectsChanged, // this, &MapEditor::updateActions); + connect(mapDocument, &MapDocument::selectedAreaChanged, + this, &MapEditor::selectedRegionChanged); if (mapView) { mZoomable = mapView->zoomable(); @@ -746,6 +748,14 @@ void MapEditor::onSelectedToolChanged(AbstractTool *tool) emit selectedToolChanged(tool); } +QRegion MapEditor::selectedRegion() const +{ + if (!mCurrentMapDocument) + return QRegion(); + + return mCurrentMapDocument->selectedArea(); +} + void MapEditor::updateActiveUndoStack() { QUndoStack *undoStack = DocumentManager::instance()->undoGroup()->activeStack(); diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index 1fc7cd5c33..ead29e30fd 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -77,6 +77,7 @@ class MapEditor final : public Editor Q_PROPERTY(Tiled::EditableMap *currentBrush READ currentBrush WRITE setCurrentBrush NOTIFY currentBrushChanged) Q_PROPERTY(Tiled::EditableMap *tileEditPreview READ tileEditPreview NOTIFY tileEditPreviewChanged) Q_PROPERTY(QRegion tileEditRegion READ tileEditRegion NOTIFY tileEditRegionChanged) + Q_PROPERTY(QRegion selectedRegion READ selectedRegion NOTIFY selectedRegionChanged) Q_PROPERTY(Tiled::EditableWangSet *currentWangSet READ currentWangSet WRITE setCurrentWangSet NOTIFY currentWangSetChanged) Q_PROPERTY(int currentWangColorIndex READ currentWangColorIndex WRITE setCurrentWangColorIndex NOTIFY currentWangColorIndexChanged) Q_PROPERTY(Tiled::MapView *currentMapView READ currentMapView CONSTANT) @@ -129,6 +130,8 @@ class MapEditor final : public Editor QRegion tileEditRegion() const; void setTileEditRegion(const QRegion ®ion); + QRegion selectedRegion() const; + EditableWangSet *currentWangSet() const; void setCurrentWangSet(EditableWangSet *wangSet); @@ -156,6 +159,7 @@ class MapEditor final : public Editor void currentBrushChanged(); void tileEditPreviewChanged(); void tileEditRegionChanged(); + void selectedRegionChanged(const QRegion &newSelection, const QRegion &oldSelection); void currentWangSetChanged(); void currentWangColorIndexChanged(int colorIndex); void selectedToolChanged(AbstractTool *tool); diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index d53da2a493..85c54d75df 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -49,7 +49,16 @@ Rectangle { } } - // TODO: Need RegionOverlay for selected tiles from tile selection tools. + RegionOverlay { + id: selectedRegionOverlay + anchors.fill: mapItem + + scale: mapContainer.scale + region: mapEditor.selectedRegion + tileSize: Qt.point(mapItem.map.tileWidth, mapItem.map.tileHeight) + + regionAlpha: 127 + } Tiled.MapBorderItem { id: mapBorderItem @@ -72,7 +81,7 @@ Rectangle { id: toolBrush anchors.left: mapItem.left anchors.top: mapItem.top - visible: singleFingerPanArea.containsMouse + visible: singleFingerPanArea.containsMouse || singleFingerPanArea.pressed property var toolPreviewMap: mapEditor.tileEditPreview map: toolPreviewMap @@ -92,7 +101,7 @@ Rectangle { RegionOverlay { id: brushRegionOverlay anchors.fill: mapItem - visible: singleFingerPanArea.containsMouse + visible: toolBrush.visible scale: mapContainer.scale region: mapEditor.tileEditRegion From a75bf94d5be970ef79f0953314f412728227fca3 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 7 Jul 2026 01:19:39 -0500 Subject: [PATCH 26/29] Added EditableMap constructor for using SharedMaps Removed obsolete setTileEditPreview(nullptr)s Moved brushMapChanged and brushRegionChanged to more intuitive locations --- src/tiled/abstracttilefilltool.cpp | 11 ++++++----- src/tiled/abstracttiletool.h | 2 +- src/tiled/editablemap.cpp | 9 ++++++++- src/tiled/editablemap.h | 2 ++ src/tiled/mapeditor.cpp | 15 +-------------- src/tiled/mapeditor.h | 2 +- src/tiled/stampbrush.cpp | 9 ++++----- 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/tiled/abstracttilefilltool.cpp b/src/tiled/abstracttilefilltool.cpp index c7bd4285c2..1518ea5212 100644 --- a/src/tiled/abstracttilefilltool.cpp +++ b/src/tiled/abstracttilefilltool.cpp @@ -229,11 +229,11 @@ void AbstractTileFillTool::updatePreview(const QRegion &fillRegion) preview->addTilesets(preview->usedTilesets()); - emit brushMapChanged(preview.get()); - emit brushRegionChanged(fillRegion); - brushItem()->setMap(preview); mPreviewMap = preview; + + emit brushMapChanged(preview); + emit brushRegionChanged(fillRegion); } bool AbstractTileFillTool::applyPreview(const QString &text) @@ -251,12 +251,13 @@ bool AbstractTileFillTool::applyPreview(const QString &text) void AbstractTileFillTool::clearOverlay() { - emit brushMapChanged(nullptr); - brushItem()->clear(); static_cast(brushItem())->setInvalidTiles(QRegion()); mPreviewMap.clear(); + + emit brushMapChanged(nullptr); + emit brushRegionChanged(QRegion()); } void AbstractTileFillTool::updateRandomListAndMissingTilesets() diff --git a/src/tiled/abstracttiletool.h b/src/tiled/abstracttiletool.h index 14b8b04e9a..31908abb85 100644 --- a/src/tiled/abstracttiletool.h +++ b/src/tiled/abstracttiletool.h @@ -63,7 +63,7 @@ class AbstractTileTool : public AbstractTool void keyPressed(QKeyEvent *event) override; signals: - void brushMapChanged(Tiled::Map *map); + void brushMapChanged(const Tiled::SharedMap &map); void brushRegionChanged(const QRegion ®ion); protected: diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index f7b47019a4..7154e7a0ab 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -72,6 +72,13 @@ EditableMap::EditableMap(const Map *map, QObject *parent) { } +EditableMap::EditableMap(const SharedMap &map, QObject *parent) + : EditableAsset(const_cast(map.data()), parent) + , mSharedMap(map) + , mReadOnly(true) +{ +} + EditableMap::EditableMap(std::unique_ptr map, QObject *parent) : EditableAsset(map.get(), parent) , mDetachedMap(std::move(map)) @@ -84,7 +91,7 @@ EditableMap::~EditableMap() detachLayer(layer); // Prevent owned object from trying to delete us again - if (mDetachedMap) + if (mDetachedMap || mSharedMap) setObject(nullptr); } diff --git a/src/tiled/editablemap.h b/src/tiled/editablemap.h index 8f36f9660a..b00cc81e6c 100644 --- a/src/tiled/editablemap.h +++ b/src/tiled/editablemap.h @@ -115,6 +115,7 @@ class TILED_EDITOR_EXPORT EditableMap final : public EditableAsset Q_INVOKABLE explicit EditableMap(QObject *parent = nullptr); explicit EditableMap(MapDocument *mapDocument, QObject *parent = nullptr); explicit EditableMap(const Map *map, QObject *parent = nullptr); + explicit EditableMap(const SharedMap &map, QObject *parent = nullptr); explicit EditableMap(std::unique_ptr map, QObject *parent = nullptr); ~EditableMap() override; @@ -242,6 +243,7 @@ class TILED_EDITOR_EXPORT EditableMap final : public EditableAsset MapRenderer *renderer() const; std::unique_ptr mDetachedMap; + SharedMap mSharedMap; mutable std::unique_ptr mRenderer; bool mReadOnly = false; diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 74a32b9a41..27fbf9dd07 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -395,11 +395,7 @@ void MapEditor::removeDocument(Document *document) Q_ASSERT(mViewForMap.contains(mapDocument)); if (mapDocument == mCurrentMapDocument) - { setCurrentDocument(nullptr); - setTileEditPreview(nullptr); - } - MapViewInterface *mapViewInterface = mViewForMap.take(mapDocument); // remove first, to keep it valid while the current widget changes @@ -1096,8 +1092,6 @@ void MapEditor::setUseOpenGL(bool useOpenGL) #ifdef TILEDQUICK_LIB void MapEditor::setUseNewHardwareRenderer(bool useNewHardwareRenderer) { - setTileEditPreview(nullptr); - for (MapViewInterface *mapViewInterface : std::as_const(mViewForMap)) { mWidgetStack->addWidget(mapViewInterface->getWidget()); @@ -1158,14 +1152,7 @@ EditableMap *MapEditor::tileEditPreview() const return mTileEditPreview.get(); } -/** -* Sets the mTileEditPreview EditableMap wrapper for the given *map. -* -* The previous *map must remain valid until after mTileEditPreview is set to -* the new *map, as EditableMap wrappers cannot be deleted if their wrapped *map -* is invalid. -*/ -void MapEditor::setTileEditPreview(Map *map) +void MapEditor::setTileEditPreview(const SharedMap &map) { if (!map) { mTileEditPreview.reset(); diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index ead29e30fd..c76378c10d 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -125,7 +125,7 @@ class MapEditor final : public Editor void setCurrentBrush(EditableMap *editableMap); EditableMap *tileEditPreview() const; - void setTileEditPreview(Map *map); + void setTileEditPreview(const SharedMap &map); QRegion tileEditRegion() const; void setTileEditRegion(const QRegion ®ion); diff --git a/src/tiled/stampbrush.cpp b/src/tiled/stampbrush.cpp index ea40194c1e..256929d544 100644 --- a/src/tiled/stampbrush.cpp +++ b/src/tiled/stampbrush.cpp @@ -91,8 +91,6 @@ void StampBrush::tilePositionChanged(QPoint pos) QVector points = pointsOnLine(mPrevTilePosition, pos); QHash paintedRegions; - emit brushMapChanged(nullptr); - for (int i = 1; i < points.size(); ++i) { drawPreviewLayer(QVector() << points.at(i)); @@ -109,6 +107,7 @@ void StampBrush::tilePositionChanged(QPoint pos) emit mapDocument()->regionEdited(ri.value(), ri.key()); } + emit brushMapChanged(nullptr); emit brushRegionChanged(mPreviewMap->modifiedTileRegion()); } else { updatePreview(); @@ -679,10 +678,10 @@ void StampBrush::updatePreview(QPoint tilePos) tileRegion = QRect(tilePos, tilePos); } - emit brushMapChanged(mPreviewMap.get()); - emit brushRegionChanged(tileRegion); - brushItem()->setMap(mPreviewMap, tileRegion); + + emit brushMapChanged(mPreviewMap); + emit brushRegionChanged(tileRegion); } void StampBrush::setRandom(bool value) From 4dfaad9907f17a0a81a09464d7c9ca2de0f330a7 Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:54:12 -0500 Subject: [PATCH 27/29] Added safeguard for new EditableMap constructor usage; Small RegionOverlay code tweaks --- src/tiled/editablemap.cpp | 6 ++++++ src/tiled/editablemap.h | 2 +- src/tiled/resources/qml/RegionOverlay.qml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tiled/editablemap.cpp b/src/tiled/editablemap.cpp index 7154e7a0ab..f09734a322 100644 --- a/src/tiled/editablemap.cpp +++ b/src/tiled/editablemap.cpp @@ -72,6 +72,12 @@ EditableMap::EditableMap(const Map *map, QObject *parent) { } +/** + * Creates a read-only instance of EditableMap that works on the given \a map. + * + * This constructor is used to send a temporary EditableMap-wrapped SharedMap to QtQuick QML. + * @warning Avoid using this constructor. Modifying or destroying the source map may lead to undefined behavior. + */ EditableMap::EditableMap(const SharedMap &map, QObject *parent) : EditableAsset(const_cast(map.data()), parent) , mSharedMap(map) diff --git a/src/tiled/editablemap.h b/src/tiled/editablemap.h index b00cc81e6c..fe90298784 100644 --- a/src/tiled/editablemap.h +++ b/src/tiled/editablemap.h @@ -243,7 +243,7 @@ class TILED_EDITOR_EXPORT EditableMap final : public EditableAsset MapRenderer *renderer() const; std::unique_ptr mDetachedMap; - SharedMap mSharedMap; + SharedMap mSharedMap = nullptr; mutable std::unique_ptr mRenderer; bool mReadOnly = false; diff --git a/src/tiled/resources/qml/RegionOverlay.qml b/src/tiled/resources/qml/RegionOverlay.qml index af6b237d08..b2e90380c9 100644 --- a/src/tiled/resources/qml/RegionOverlay.qml +++ b/src/tiled/resources/qml/RegionOverlay.qml @@ -1,12 +1,12 @@ import QtQuick import QtQuick.Shapes -import QtQuick.Controls import org.mapeditor.Tiled as Tiled Tiled.RegionOverlay { id: regionOverlay property var scale + // TODO: Draw only visible area Shape { ShapePath { From eee95ff33941f878ba7d146c206dea4fd73e5a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= Date: Wed, 8 Jul 2026 17:41:27 +0200 Subject: [PATCH 28/29] Fixed crash by only forwarding Quick mouse events to an active tool The selected tool is only activated on a scene when there is a view using it, so the TILEDQUICK_LIB mouse handlers should not call into the tool when mViewWithTool is not set. Added MapEditor::activeTool, which checks this, and used it in place of selectedTool in these handlers. --- src/tiled/mapeditor.cpp | 34 ++++++++++++++++++++++------------ src/tiled/mapeditor.h | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 27fbf9dd07..6b598a365f 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -1223,18 +1223,27 @@ void MapEditor::setSelectedTool(AbstractTool *tool) mToolManager->selectTool(tool); } +AbstractTool *MapEditor::activeTool() const +{ + if (!mViewWithTool) + return nullptr; + return mToolManager->selectedTool(); +} + #ifdef TILEDQUICK_LIB void MapEditor::setQuickMouseCoords(QPointF coords) { - if (!selectedTool()) + auto tool = activeTool(); + if (!tool) return; - selectedTool()->mouseMoved(coords, Qt::NoModifier); + tool->mouseMoved(coords, Qt::NoModifier); } void MapEditor::quickMousePressed(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos) { - if (!selectedTool()) + auto tool = activeTool(); + if (!tool) return; QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); @@ -1245,12 +1254,13 @@ void MapEditor::quickMousePressed(Qt::MouseButton button, Qt::MouseButtons butto event.setScenePos(scenePos); event.setScreenPos(screenPos); - selectedTool()->mousePressed(&event); + tool->mousePressed(&event); } void MapEditor::quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos) { - if (!selectedTool()) + auto tool = activeTool(); + if (!tool) return; QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); @@ -1261,19 +1271,19 @@ void MapEditor::quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons butt event.setScenePos(scenePos); event.setScreenPos(screenPos); - selectedTool()->mouseReleased(&event); + tool->mouseReleased(&event); } void MapEditor::quickContainsMouseChanged(bool viewContainsMouse) { - if (!selectedTool()) + auto tool = activeTool(); + if (!tool) return; - if (viewContainsMouse) { - selectedTool()->mouseEntered(); - } else { - selectedTool()->mouseLeft(); - } + if (viewContainsMouse) + tool->mouseEntered(); + else + tool->mouseLeft(); } #endif diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index c76378c10d..e091d32c8b 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -145,6 +145,7 @@ class MapEditor final : public Editor AbstractTool *selectedTool() const; void setSelectedTool(AbstractTool *tool); + AbstractTool *activeTool() const; #ifdef TILEDQUICK_LIB Q_INVOKABLE void setQuickMouseCoords(QPointF coords); From 8dd9411b31d2650cdd8b44711a4bebb014d9a26e Mon Sep 17 00:00:00 2001 From: Dagon Williams <43974011+UltraDagon@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:16:23 -0500 Subject: [PATCH 29/29] Added requested changes Removed unnecessary Qt.core dependency from libtiledquick.qbs Removed redundant RegionOverlay mRegion and mMapRect initializations Changed TileLayerItem::layer to be inline Renamed MapEditor's setQuickMouseCoords to quickMouseMoved Removed redundant EditableMap mSharedMap default assignment Removed unnecessary setRegion and translateRegion functions from BrushItem Removed TODO comment from RegionOverlay Shortened mapRelativeCoords function in mapview.qml Removed unused cursorTileCoords function from mapview.qml Changed mTileEditPreview to be a SharedMap Changed tileEditPreview to create the new editableMap instance on request --- src/libtiledquick/libtiledquick.qbs | 2 -- src/libtiledquick/regionoverlay.cpp | 2 -- src/libtiledquick/tilelayeritem.cpp | 4 ---- src/libtiledquick/tilelayeritem.h | 4 ++++ src/tiled/brushitem.cpp | 29 +++++++---------------- src/tiled/brushitem.h | 2 -- src/tiled/mapeditor.cpp | 22 ++++++++++++----- src/tiled/mapeditor.h | 4 ++-- src/tiled/resources/qml/RegionOverlay.qml | 1 - src/tiled/resources/qml/mapview.qml | 11 ++------- 10 files changed, 32 insertions(+), 49 deletions(-) diff --git a/src/libtiledquick/libtiledquick.qbs b/src/libtiledquick/libtiledquick.qbs index 3a5721a188..5ba7fa55d1 100644 --- a/src/libtiledquick/libtiledquick.qbs +++ b/src/libtiledquick/libtiledquick.qbs @@ -73,8 +73,6 @@ DynamicLibrary { cpp.includePaths: exportingProduct.sourceDirectory } - Depends { name: "Qt.core" } - install: !qbs.targetOS.contains("darwin") installDir: { if (qbs.targetOS.contains("windows")) diff --git a/src/libtiledquick/regionoverlay.cpp b/src/libtiledquick/regionoverlay.cpp index 8f6d40f648..b24de19137 100644 --- a/src/libtiledquick/regionoverlay.cpp +++ b/src/libtiledquick/regionoverlay.cpp @@ -29,8 +29,6 @@ using namespace TiledQuick; RegionOverlay::RegionOverlay(QQuickItem *parent) : QQuickItem(parent) , mTileSize(0, 0) - , mRegion(QRegion()) - , mMapRect(QRect()) , mValidColor(QApplication::palette().highlight().color()) , mInvalidColor(QColor(255,0,0)) , mRegionAlpha(64) diff --git a/src/libtiledquick/tilelayeritem.cpp b/src/libtiledquick/tilelayeritem.cpp index 185eae6ab6..fad6714de9 100644 --- a/src/libtiledquick/tilelayeritem.cpp +++ b/src/libtiledquick/tilelayeritem.cpp @@ -199,10 +199,6 @@ QSGNode *TileLayerItem::updatePaintNode(QSGNode *node, return node; } -Tiled::TileLayer *TileLayerItem::layer() { - return mLayer; -} - void TileLayerItem::updateVisibleTiles() { const MapItem *mapItem = static_cast(parentItem()); diff --git a/src/libtiledquick/tilelayeritem.h b/src/libtiledquick/tilelayeritem.h index 02626f5c94..82c0ca6772 100644 --- a/src/libtiledquick/tilelayeritem.h +++ b/src/libtiledquick/tilelayeritem.h @@ -94,6 +94,10 @@ class TileItem : public QQuickItem QPoint mPosition; }; +inline Tiled::TileLayer *TileLayerItem::layer() { + return mLayer; +} + inline QPoint TileItem::position() const { return mPosition; diff --git a/src/tiled/brushitem.cpp b/src/tiled/brushitem.cpp index 901798ec87..23056b6d31 100644 --- a/src/tiled/brushitem.cpp +++ b/src/tiled/brushitem.cpp @@ -61,7 +61,7 @@ void BrushItem::clear() { mTileLayer.clear(); mMap.clear(); - setRegion(QRegion()); + mRegion = QRegion(); updateBoundingRect(); update(); @@ -86,7 +86,7 @@ void BrushItem::setTileLayer(const SharedTileLayer &tileLayer, const QRegion ®ion) { mTileLayer = tileLayer; - setRegion(region); + mRegion = region; updateBoundingRect(); update(); @@ -100,7 +100,7 @@ void BrushItem::setMap(const SharedMap &map) void BrushItem::setMap(const SharedMap &map, const QRegion ®ion) { mMap = map; - setRegion(region); + mRegion = region; updateBoundingRect(); update(); @@ -118,7 +118,7 @@ void BrushItem::setTileLayerPosition(QPoint pos) if (oldPosition == pos) return; - translateRegion(pos - oldPosition); + mRegion.translate(pos - oldPosition); mTileLayer->setX(pos.x()); mTileLayer->setY(pos.y()); updateBoundingRect(); @@ -129,7 +129,10 @@ void BrushItem::setTileLayerPosition(QPoint pos) */ void BrushItem::setTileRegion(const QRegion ®ion) { - setRegion(region); + if (mRegion == region) + return; + + mRegion = region; updateBoundingRect(); } @@ -233,19 +236,3 @@ void BrushItem::updateBoundingRect() // Adjust for border drawn at tile selection edges mBoundingRect.adjust(-1, -1, 1, 1); } - -void BrushItem::setRegion(const QRegion ®ion) -{ - if (mRegion == region) - return; - - mRegion = region; -} - -void BrushItem::translateRegion(const QPoint &p) -{ - if (p.x() == 0 && p.y() == 0) - return; - - mRegion.translate(p); -} diff --git a/src/tiled/brushitem.h b/src/tiled/brushitem.h index 3f2028c290..8c313cad2f 100644 --- a/src/tiled/brushitem.h +++ b/src/tiled/brushitem.h @@ -69,8 +69,6 @@ class BrushItem : public QGraphicsItem private: void updateBoundingRect(); - void setRegion(const QRegion ®ion); - void translateRegion(const QPoint &p); MapDocument *mMapDocument; SharedTileLayer mTileLayer; diff --git a/src/tiled/mapeditor.cpp b/src/tiled/mapeditor.cpp index 6b598a365f..773bced1db 100644 --- a/src/tiled/mapeditor.cpp +++ b/src/tiled/mapeditor.cpp @@ -1149,17 +1149,27 @@ EditableMap *MapEditor::currentBrush() const EditableMap *MapEditor::tileEditPreview() const { - return mTileEditPreview.get(); + EditableMap* preview; + EditableObject* existingPreview = EditableObject::find(mTileEditPreview.get()); + + if (existingPreview) + preview = qobject_cast(existingPreview); + else + preview = mTileEditPreview ? new EditableMap(mTileEditPreview) : nullptr; + + return preview; } void MapEditor::setTileEditPreview(const SharedMap &map) { - if (!map) { - mTileEditPreview.reset(); + if (map == mTileEditPreview) return; - } - mTileEditPreview = std::make_unique(map); + EditableObject* oldPreview = EditableObject::find(mTileEditPreview.get()); + if (oldPreview) + delete oldPreview; + + mTileEditPreview = map; emit tileEditPreviewChanged(); } @@ -1231,7 +1241,7 @@ AbstractTool *MapEditor::activeTool() const } #ifdef TILEDQUICK_LIB -void MapEditor::setQuickMouseCoords(QPointF coords) +void MapEditor::quickMouseMoved(QPointF coords) { auto tool = activeTool(); if (!tool) diff --git a/src/tiled/mapeditor.h b/src/tiled/mapeditor.h index e091d32c8b..eb72525851 100644 --- a/src/tiled/mapeditor.h +++ b/src/tiled/mapeditor.h @@ -148,7 +148,7 @@ class MapEditor final : public Editor AbstractTool *activeTool() const; #ifdef TILEDQUICK_LIB - Q_INVOKABLE void setQuickMouseCoords(QPointF coords); + Q_INVOKABLE void quickMouseMoved(QPointF coords); Q_INVOKABLE void quickMousePressed(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); Q_INVOKABLE void quickMouseReleased(Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, QPointF pos, QPointF scenePos, QPoint screenPos); Q_INVOKABLE void quickContainsMouseChanged(bool viewContainsMouse); @@ -234,7 +234,7 @@ class MapEditor final : public Editor WangBrush *mWangBrush; EditPolygonTool *mEditPolygonTool; - std::unique_ptr mTileEditPreview; + SharedMap mTileEditPreview; QRegion mTileEditRegion; QToolBar *mMainToolBar; diff --git a/src/tiled/resources/qml/RegionOverlay.qml b/src/tiled/resources/qml/RegionOverlay.qml index b2e90380c9..b32a0370ab 100644 --- a/src/tiled/resources/qml/RegionOverlay.qml +++ b/src/tiled/resources/qml/RegionOverlay.qml @@ -6,7 +6,6 @@ Tiled.RegionOverlay { id: regionOverlay property var scale - // TODO: Draw only visible area Shape { ShapePath { diff --git a/src/tiled/resources/qml/mapview.qml b/src/tiled/resources/qml/mapview.qml index 85c54d75df..5de5a3f48f 100644 --- a/src/tiled/resources/qml/mapview.qml +++ b/src/tiled/resources/qml/mapview.qml @@ -196,18 +196,11 @@ Rectangle { } } - function cursorTileCoords() { - var tileCoords = mapItem.screenToTileCoords(mapRelativeCoords.x, mapRelativeCoords.y) - - return tileCoords - } - property var mapRelativeCoords: { - var mapRelativeCoords = singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY) - return mapRelativeCoords + return singleFingerPanArea.mapToItem(mapItem, singleFingerPanArea.mouseX, singleFingerPanArea.mouseY) } onMapRelativeCoordsChanged: { - mapEditor.setQuickMouseCoords(mapRelativeCoords) + mapEditor.quickMouseMoved(mapRelativeCoords) } }