diff --git a/CHANGELOG.md b/CHANGELOG.md index 861883dc..c1cea51d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - Some new shortcuts were added in [porymap/#290](https://github.com/huderlem/porymap/pull/290). - All plain text boxes now have a clear button to delete the text. - The window sizes and positions of the tileset editor, palette editor, and region map editor are now stored in `porymap.cfg`. +- Add ruler tool for measuring metatile distance in events tab (Right-click to turn on/off, left-click to lock in place). ### Changed - Holding `shift` now toggles "Smart Path" drawing; when the "Smart Paths" checkbox is checked, holding `shift` will temporarily disable it. diff --git a/docsrc/manual/editing-map-events.rst b/docsrc/manual/editing-map-events.rst index 48602a19..4a7ee536 100644 --- a/docsrc/manual/editing-map-events.rst +++ b/docsrc/manual/editing-map-events.rst @@ -237,17 +237,32 @@ The event editing tab also extends functionality to a few of the tool buttons de A brief description and animation is listed for each of the available tools below: Pencil - When clicking on an existing event, the pencil tool will behave normally (as the standard cursor). It can also be used to "draw" events in a certain location. The event created will be a default-valued event of the same type as the currently selected event. + When clicking on an existing event, the pencil tool will behave normally (as the standard cursor). It can also be used to "draw" events in a certain location. The event created will be a default-valued event of the same type as the currently selected event. Right-clicking with the Pencil Tool will return to the Pointer tool. .. figure:: images/editing-map-events/event-tool-pencil.gif :alt: Drawing Object Events with the Pencil Tool Drawing Object Events with the Pencil Tool +Pointer + The Pointer Tool is the default tool for the event editing tab and allows you to select and move events on the map. The Pointer Tool also gives you access to the :ref:`Ruler Tool `. + Shift - You can use the shift tool to move any number of events together. When a selected event is dragged, all other selected events will move with it. When a tile with no event is clicked, all events on the map can be dragged. + You can use the Shift Tool to move any number of events together. When a selected event is dragged, all other selected events will move with it. When a tile with no event is clicked, all events on the map can be dragged. .. figure:: images/editing-map-events/event-tool-shift.gif :alt: Moving Events with the Shift Tool Moving Events with the Shift Tool + +.. _ruler-tool: + +Ruler Tool +---------- + +The Ruler Tool provides a convenient way to measure distance on the map. This is particularly useful for scripting object movement. With the Pointer Tool selected you can activate the ruler with a Right-click. With the ruler active you can drag the mouse around to extend the ruler. The ruler can be deactivated with another Right-click, or locked in place with a Left-click (Left-click again to unlock the ruler). The dimensions of the ruler are displayed in a tool-tip and in the status bar in the bottom left corner of the widnow. + +.. figure:: images/editing-map-events/event-tool-ruler.gif + :alt: Measuring metatile distance with the Ruler Tool + + Measuring metatile distance with the Ruler Tool diff --git a/docsrc/manual/images/editing-map-events/event-tool-ruler.gif b/docsrc/manual/images/editing-map-events/event-tool-ruler.gif new file mode 100644 index 00000000..fdbbd12f Binary files /dev/null and b/docsrc/manual/images/editing-map-events/event-tool-ruler.gif differ diff --git a/docsrc/manual/shortcuts.rst b/docsrc/manual/shortcuts.rst index 01f5b01c..0acd894d 100644 --- a/docsrc/manual/shortcuts.rst +++ b/docsrc/manual/shortcuts.rst @@ -38,10 +38,10 @@ Main Window :header: Map Editing :widths: 20, 20 - Select Tiles From Map, Hold Right-click while dragging + Select Metatile, Right-click in "paint" or "fill" mode + Select Multiple Metatiles, Hold Right-click while dragging Bucket Fill Metatiles, Middle-click from "paint" or "fill" mode Magic Fill Metatiles, ``Ctrl+`` Middle-click from "paint" or "fill" mode - Select Metatile, Right-click in "paint" or "fill" mode Zoom In, ``Ctrl++`` `or` ``Ctrl+=`` Zoom Out, ``Ctrl+-`` Reset View Scale, ``Ctrl+0`` @@ -52,8 +52,9 @@ Main Window Draw Straight Paths, Hold ``Ctrl`` Duplicate Event(s), ``Ctrl+D`` while selected Delete Event(s), ``DEL`` while selected - Select Event, Right-click in paint mode - Update Event Selection, `Ctrl+` click + Pointer, Right-click in "paint" mode + Select Event, Left-click in "pointer" mode or Right-click in "paint" mode + Select Multiple Events, `Ctrl+` click diff --git a/include/core/map.h b/include/core/map.h index 867b5bc8..4d34a071 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -113,6 +113,7 @@ private: signals: void mapChanged(Map *map); + void mapDimensionsChanged(const QSize &size); void mapNeedsRedrawing(); }; diff --git a/include/core/metatile.h b/include/core/metatile.h index 77a3c687..27d515ba 100644 --- a/include/core/metatile.h +++ b/include/core/metatile.h @@ -3,6 +3,7 @@ #include "tile.h" #include +#include #include class Metatile @@ -20,6 +21,7 @@ public: Metatile *copy(); void copyInPlace(Metatile*); static int getBlockIndex(int); + static QPoint coordFromPixmapCoord(const QPointF &pixelCoord); }; #endif // METATILE_H diff --git a/include/editor.h b/include/editor.h index b887d558..fc8cfe26 100644 --- a/include/editor.h +++ b/include/editor.h @@ -23,6 +23,7 @@ #include "settings.h" #include "movablerect.h" #include "cursortilerect.h" +#include "mapruler.h" class DraggablePixmapItem; class MetatilesPixmapItem; @@ -117,6 +118,7 @@ public: QList gridLines; MovableRect *playerViewRect = nullptr; CursorTileRect *cursorMapTileRect = nullptr; + MapRuler *map_ruler = nullptr; QGraphicsScene *scene_metatiles = nullptr; QGraphicsScene *scene_current_metatile_selection = nullptr; @@ -196,12 +198,13 @@ private slots: void onHoveredMovementPermissionCleared(); void onHoveredMetatileSelectionChanged(uint16_t); void onHoveredMetatileSelectionCleared(); - void onHoveredMapMetatileChanged(int, int); + void onHoveredMapMetatileChanged(const QPoint &pos); void onHoveredMapMetatileCleared(); void onHoveredMapMovementPermissionChanged(int, int); void onHoveredMapMovementPermissionCleared(); void onSelectedMetatilesChanged(); void onWheelZoom(int); + void onMapRulerLengthChanged(); signals: void objectsChanged(); diff --git a/include/ui/mappixmapitem.h b/include/ui/mappixmapitem.h index e8a1cd1c..e9d999b4 100644 --- a/include/ui/mappixmapitem.h +++ b/include/ui/mappixmapitem.h @@ -77,7 +77,7 @@ public: void updateMetatileSelection(QGraphicsSceneMouseEvent *event); void paintNormal(int x, int y, bool fromScriptCall = false); void lockNondominantAxis(QGraphicsSceneMouseEvent *event); - int adjustCoord(int coord, MapPixmapItem::Axis axis); + QPoint adjustCoords(QPoint pos); private: void paintSmartPath(int x, int y, bool fromScriptCall = false); @@ -89,7 +89,7 @@ signals: void startPaint(QGraphicsSceneMouseEvent *, MapPixmapItem *); void endPaint(QGraphicsSceneMouseEvent *, MapPixmapItem *); void mouseEvent(QGraphicsSceneMouseEvent *, MapPixmapItem *); - void hoveredMapMetatileChanged(int x, int y); + void hoveredMapMetatileChanged(const QPoint &pos); void hoveredMapMetatileCleared(); protected: diff --git a/include/ui/mapruler.h b/include/ui/mapruler.h new file mode 100644 index 00000000..0ee6e698 --- /dev/null +++ b/include/ui/mapruler.h @@ -0,0 +1,81 @@ +#ifndef MAPRULER_H +#define MAPRULER_H + +#include +#include +#include + + +class MapRuler : public QGraphicsObject, private QLine +{ + Q_OBJECT + +public: + MapRuler(QColor innerColor = Qt::yellow, QColor borderColor = Qt::black) : + innerColor(innerColor), + borderColor(borderColor), + mapSize(QSize()) + { + init(); + } + void init(); + QRectF boundingRect() const override; + QPainterPath shape() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override; + bool eventFilter(QObject *, QEvent *event) override; + + void setEnabled(bool enabled); + bool isAnchored() const { return anchored; } + bool isLocked() const { return locked; } + + // Ruler start point in metatiles + QPoint anchor() const { return QLine::p1(); } + // Ruler end point in metatiles + QPoint endPos() const { return QLine::p2(); } + // X-coordinate of the ruler's left edge in metatiles + int left() const { return qMin(anchor().x(), endPos().x()); } + // Y-coordinate of the ruler's top edge in metatiles + int top() const { return qMin(anchor().y(), endPos().y()); } + // Horizontal component of the ruler in metatiles + int deltaX() const { return QLine::dx(); } + // Vertical component of the ruler in metatiles + int deltaY() const { return QLine::dy(); } + // Ruler width in metatiles + int width() const { return qAbs(deltaX()); } + // Ruler height in metatiles + int height() const { return qAbs(deltaY()); } + + QString statusMessage; + +public slots: + void mouseEvent(QGraphicsSceneMouseEvent *event); + void setMapDimensions(const QSize &size); + +private: + QColor innerColor; + QColor borderColor; + QSize mapSize; + QRect xRuler; + QRect yRuler; + QLineF cornerTick; + bool anchored; + bool locked; + + static int thickness; + + QPoint snapToWithinBounds(QPoint pos) const; + void setAnchor(const QPointF &scenePos, const QPoint &screenPos); + void endAnchor(); + void setEndPos(const QPointF &scenePos, const QPoint &screenPos); + void showDimensions(const QPoint &screenPos) const; + void hideDimensions() const; + void updateGeometry(); + int pixWidth() const { return width() * 16; } + int pixHeight() const { return height() * 16; } + +signals: + void lengthChanged(); + void deactivated(const QPoint &endPos); +}; + +#endif // MAPRULER_H diff --git a/porymap.pro b/porymap.pro index da274dba..0da4a14b 100644 --- a/porymap.pro +++ b/porymap.pro @@ -68,7 +68,9 @@ SOURCES += src/core/block.cpp \ src/ui/regionmapeditor.cpp \ src/ui/newmappopup.cpp \ src/ui/mapimageexporter.cpp \ + src/ui/newtilesetdialog.cpp \ src/ui/flowlayout.cpp \ + src/ui/mapruler.cpp \ src/config.cpp \ src/editor.cpp \ src/main.cpp \ @@ -76,8 +78,7 @@ SOURCES += src/core/block.cpp \ src/project.cpp \ src/scripting.cpp \ src/settings.cpp \ - src/log.cpp \ - src/ui/newtilesetdialog.cpp + src/log.cpp HEADERS += include/core/block.h \ include/core/blockdata.h \ @@ -135,16 +136,17 @@ HEADERS += include/core/block.h \ include/ui/regionmapeditor.h \ include/ui/newmappopup.h \ include/ui/mapimageexporter.h \ + include/ui/newtilesetdialog.h \ + include/ui/overlay.h \ include/ui/flowlayout.h \ + include/ui/mapruler.h \ include/config.h \ include/editor.h \ include/mainwindow.h \ include/project.h \ include/scripting.h \ include/settings.h \ - include/log.h \ - include/ui/newtilesetdialog.h \ - include/ui/overlay.h + include/log.h FORMS += forms/mainwindow.ui \ forms/eventpropertiesframe.ui \ diff --git a/src/core/map.cpp b/src/core/map.cpp index 3db6daeb..02cd15f8 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -353,6 +353,7 @@ void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata) { layout->height = QString::number(newHeight); emit mapChanged(this); + emit mapDimensionsChanged(QSize(getWidth(), getHeight())); } void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata) { diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp index 7bf3030d..e870dcad 100644 --- a/src/core/metatile.cpp +++ b/src/core/metatile.cpp @@ -39,3 +39,9 @@ int Metatile::getBlockIndex(int index) { return index - Project::getNumMetatilesPrimary(); } } + +QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) { + int x = static_cast(pixelCoord.x()) / 16; + int y = static_cast(pixelCoord.y()) / 16; + return QPoint(x, y); +} diff --git a/src/editor.cpp b/src/editor.cpp index 2471f32d..619abe3a 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -6,6 +6,7 @@ #include "mapconnection.h" #include "currentselectedmetatilespixmapitem.h" #include "mapsceneeventfilter.h" +#include "metatile.h" #include "montabwidget.h" #include "editcommands.h" #include @@ -23,6 +24,9 @@ Editor::Editor(Ui::MainWindow* ui) this->settings = new Settings(); this->playerViewRect = new MovableRect(&this->settings->playerViewRectEnabled, 30 * 8, 20 * 8, qRgb(255, 255, 255)); this->cursorMapTileRect = new CursorTileRect(&this->settings->cursorTileRectEnabled, qRgb(255, 255, 255)); + this->map_ruler = new MapRuler(); + connect(this->map_ruler, &MapRuler::lengthChanged, this, &Editor::onMapRulerLengthChanged); + connect(this->map_ruler, &MapRuler::deactivated, this, &Editor::onHoveredMapMetatileChanged); /// Instead of updating the selected events after every single undo action /// (eg when the user rolls back several at once), only reselect events when @@ -41,6 +45,7 @@ Editor::~Editor() delete this->settings; delete this->playerViewRect; delete this->cursorMapTileRect; + delete this->map_ruler; closeProject(); } @@ -910,32 +915,45 @@ void Editor::scaleMapView(int s) { } } -void Editor::onHoveredMapMetatileChanged(int x, int y) { - this->playerViewRect->updateLocation(x, y); - this->cursorMapTileRect->updateLocation(x, y); +void Editor::onHoveredMapMetatileChanged(const QPoint &pos) { + this->playerViewRect->updateLocation(pos.x(), pos.y()); + this->cursorMapTileRect->updateLocation(pos.x(), pos.y()); if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles - && x >= 0 && x < map->getWidth() && y >= 0 && y < map->getHeight()) { - int blockIndex = y * map->getWidth() + x; + && pos.x() >= 0 && pos.x() < map->getWidth() && pos.y() >= 0 && pos.y() < map->getHeight()) { + int blockIndex = pos.y() * map->getWidth() + pos.x(); int metatileId = map->layout->blockdata->blocks->at(blockIndex).tile; this->ui->statusBar->showMessage(QString("X: %1, Y: %2, %3, Scale = %4x") - .arg(x) - .arg(y) + .arg(pos.x()) + .arg(pos.y()) .arg(getMetatileDisplayMessage(metatileId)) .arg(QString::number(pow(scale_base, scale_exp), 'g', 2))); } else if (map_item->paintingMode == MapPixmapItem::PaintMode::EventObjects - && x >= 0 && x < map->getWidth() && y >= 0 && y < map->getHeight()) { - this->ui->statusBar->showMessage(QString("X: %1, Y: %2") - .arg(x) - .arg(y)); + && pos.x() >= 0 && pos.x() < map->getWidth() && pos.y() >= 0 && pos.y() < map->getHeight()) { + this->ui->statusBar->showMessage(QString("X: %1, Y: %2, Scale = %3x") + .arg(pos.x()) + .arg(pos.y()) + .arg(QString::number(pow(scale_base, scale_exp), 'g', 2))); } } +void Editor::onMapRulerLengthChanged() { + const QPoint pos = map_ruler->endPos(); + ui->statusBar->showMessage(QString("X: %1, Y: %2, Scale = %3x; %4") + .arg(pos.x()) + .arg(pos.y()) + .arg(QString::number(pow(scale_base, scale_exp), 'g', 2)) + .arg(map_ruler->statusMessage)); +} + void Editor::onHoveredMapMetatileCleared() { this->playerViewRect->setVisible(false); this->cursorMapTileRect->setVisible(false); if (map_item->paintingMode == MapPixmapItem::PaintMode::Metatiles || map_item->paintingMode == MapPixmapItem::PaintMode::EventObjects) { this->ui->statusBar->clearMessage(); + if (this->map_ruler->isAnchored()) { + this->ui->statusBar->showMessage(this->map_ruler->statusMessage); + } } } @@ -1005,6 +1023,8 @@ bool Editor::setMap(QString map_name) { if (!displayMap()) { return false; } + map_ruler->setMapDimensions(QSize(map->getWidth(), map->getHeight())); + connect(map, &Map::mapDimensionsChanged, map_ruler, &MapRuler::setMapDimensions); updateSelectedEvents(); } @@ -1012,17 +1032,15 @@ bool Editor::setMap(QString map_name) { } void Editor::onMapStartPaint(QGraphicsSceneMouseEvent *event, MapPixmapItem *item) { - if (!(item->paintingMode == MapPixmapItem::PaintMode::Metatiles)) { + if (item->paintingMode != MapPixmapItem::PaintMode::Metatiles) { return; } - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (event->buttons() & Qt::RightButton && (map_edit_mode == "paint" || map_edit_mode == "fill")) { - this->cursorMapTileRect->initRightClickSelectionAnchor(x, y); + this->cursorMapTileRect->initRightClickSelectionAnchor(pos.x(), pos.y()); } else { - this->cursorMapTileRect->initAnchor(x, y); + this->cursorMapTileRect->initAnchor(pos.x(), pos.y()); } } @@ -1066,9 +1084,7 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item return; } - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (item->paintingMode == MapPixmapItem::PaintMode::Metatiles) { if (map_edit_mode == "paint") { @@ -1085,8 +1101,7 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); - x = item->adjustCoord(x, MapPixmapItem::Axis::X); - y = item->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = item->adjustCoords(pos); } item->paint(event); } @@ -1110,8 +1125,7 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); - x = item->adjustCoord(x, MapPixmapItem::Axis::X); - y = item->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = item->adjustCoords(pos); } item->shift(event); } @@ -1135,7 +1149,7 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item if (eventType != EventType::HealLocation) { DraggablePixmapItem * newEvent = addNewEvent(eventType); if (newEvent) { - newEvent->move(x, y); + newEvent->move(pos.x(), pos.y()); emit objectsChanged(); selectMapEvent(newEvent, false); } @@ -1152,18 +1166,18 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item actionId++; } else { if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); } else if (event->type() == QEvent::GraphicsSceneMouseMove) { - if (x != selection_origin.x() || y != selection_origin.y()) { - int xDelta = x - selection_origin.x(); - int yDelta = y - selection_origin.y(); + if (pos.x() != selection_origin.x() || pos.y() != selection_origin.y()) { + int xDelta = pos.x() - selection_origin.x(); + int yDelta = pos.y() - selection_origin.y(); QList selectedEvents; for (DraggablePixmapItem *item : getObjects()) { selectedEvents.append(item->event); } - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); map->editHistory.push(new EventShift(selectedEvents, xDelta, yDelta, actionId)); } @@ -1171,8 +1185,8 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } } } - this->playerViewRect->updateLocation(x, y); - this->cursorMapTileRect->updateLocation(x, y); + this->playerViewRect->updateLocation(pos.x(), pos.y()); + this->cursorMapTileRect->updateLocation(pos.x(), pos.y()); } void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item) { @@ -1180,9 +1194,7 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm return; } - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (map_edit_mode == "paint") { if (event->buttons() & Qt::RightButton) { @@ -1197,8 +1209,7 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); - x = item->adjustCoord(x, MapPixmapItem::Axis::X); - y = item->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = item->adjustCoords(pos); } item->paint(event); } @@ -1218,13 +1229,12 @@ void Editor::mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixm this->setStraightPathCursorMode(event); if (this->cursorMapTileRect->getStraightPathMode()) { item->lockNondominantAxis(event); - x = item->adjustCoord(x, MapPixmapItem::Axis::X); - y = item->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = item->adjustCoords(pos); } item->shift(event); } - this->playerViewRect->updateLocation(x, y); - this->cursorMapTileRect->updateLocation(x, y); + this->playerViewRect->updateLocation(pos.x(), pos.y()); + this->cursorMapTileRect->updateLocation(pos.x(), pos.y()); } bool Editor::displayMap() { @@ -1233,6 +1243,7 @@ bool Editor::displayMap() { MapSceneEventFilter *filter = new MapSceneEventFilter(); scene->installEventFilter(filter); connect(filter, &MapSceneEventFilter::wheelZoom, this, &Editor::onWheelZoom); + scene->installEventFilter(this->map_ruler); } if (map_item && scene) { @@ -1240,6 +1251,7 @@ bool Editor::displayMap() { delete map_item; scene->removeItem(this->playerViewRect); scene->removeItem(this->cursorMapTileRect); + scene->removeItem(this->map_ruler); } displayMetatileSelector(); @@ -1256,8 +1268,10 @@ bool Editor::displayMap() { this->playerViewRect->setZValue(1000); this->cursorMapTileRect->setZValue(1001); + this->map_ruler->setZValue(1002); scene->addItem(this->playerViewRect); scene->addItem(this->cursorMapTileRect); + scene->addItem(this->map_ruler); if (map_item) { map_item->setVisible(false); @@ -1303,8 +1317,8 @@ void Editor::displayMapMetatiles() { this, SLOT(onMapStartPaint(QGraphicsSceneMouseEvent*,MapPixmapItem*))); connect(map_item, SIGNAL(endPaint(QGraphicsSceneMouseEvent*,MapPixmapItem*)), this, SLOT(onMapEndPaint(QGraphicsSceneMouseEvent*,MapPixmapItem*))); - connect(map_item, SIGNAL(hoveredMapMetatileChanged(int, int)), - this, SLOT(onHoveredMapMetatileChanged(int, int))); + connect(map_item, SIGNAL(hoveredMapMetatileChanged(const QPoint&)), + this, SLOT(onHoveredMapMetatileChanged(const QPoint&))); connect(map_item, SIGNAL(hoveredMapMetatileCleared()), this, SLOT(onHoveredMapMetatileCleared())); @@ -1996,8 +2010,8 @@ void Editor::objectsView_onMousePress(QMouseEvent *event) { if (map_item && map_item->paintingMode != MapPixmapItem::PaintMode::EventObjects) { return; } - if (this->map_edit_mode == "paint" && event->buttons() & Qt::RightButton) { - this->map_edit_mode = "select"; + if (this->obj_edit_mode == "paint" && event->buttons() & Qt::RightButton) { + this->obj_edit_mode = "select"; this->settings->mapCursor = QCursor(); this->cursorMapTileRect->setSingleTileMode(); this->ui->toolButton_Paint->setChecked(false); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a477aaa2..45037f10 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1356,6 +1356,9 @@ void MainWindow::on_mainTabBar_tabBarClicked(int index) if (projectConfig.getEncounterJsonActive()) editor->saveEncounterTabData(); } + if (index != 1) { + editor->map_ruler->setEnabled(false); + } } void MainWindow::on_actionZoom_In_triggered() { @@ -2212,10 +2215,15 @@ void MainWindow::on_toolButton_Shift_clicked() void MainWindow::checkToolButtons() { QString edit_mode; - if (ui->mainTabBar->currentIndex() == 0) + if (ui->mainTabBar->currentIndex() == 0) { edit_mode = editor->map_edit_mode; - else + } else { edit_mode = editor->obj_edit_mode; + if (edit_mode == "select" && editor->map_ruler) + editor->map_ruler->setEnabled(true); + else if (editor->map_ruler) + editor->map_ruler->setEnabled(false); + } ui->toolButton_Paint->setChecked(edit_mode == "paint"); ui->toolButton_Select->setChecked(edit_mode == "select"); diff --git a/src/ui/bordermetatilespixmapitem.cpp b/src/ui/bordermetatilespixmapitem.cpp index c741bb53..1c34e707 100644 --- a/src/ui/bordermetatilespixmapitem.cpp +++ b/src/ui/bordermetatilespixmapitem.cpp @@ -1,22 +1,21 @@ #include "bordermetatilespixmapitem.h" #include "imageproviders.h" +#include "metatile.h" #include "editcommands.h" #include void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); int width = map->getBorderWidth(); int height = map->getBorderHeight(); Blockdata *oldBorder = map->layout->border->copy(); - for (int i = 0; i < selectionDimensions.x() && (i + x) < width; i++) { - for (int j = 0; j < selectionDimensions.y() && (j + y) < height; j++) { - int blockIndex = (j + y) * width + (i + x); + for (int i = 0; i < selectionDimensions.x() && (i + pos.x()) < width; i++) { + for (int j = 0; j < selectionDimensions.y() && (j + pos.y()) < height; j++) { + int blockIndex = (j + pos.y()) * width + (i + pos.x()); uint16_t tile = selectedMetatiles->at(j * selectionDimensions.x() + i); (*map->layout->border->blocks)[blockIndex].tile = tile; } diff --git a/src/ui/collisionpixmapitem.cpp b/src/ui/collisionpixmapitem.cpp index 52b025b3..48b8b368 100644 --- a/src/ui/collisionpixmapitem.cpp +++ b/src/ui/collisionpixmapitem.cpp @@ -1,5 +1,6 @@ #include "collisionpixmapitem.h" #include "editcommands.h" +#include "metatile.h" void CollisionPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { int x = static_cast(event->pos().x()) / 16; @@ -18,11 +19,9 @@ void CollisionPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { } void CollisionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - this->paint_tile_initial_x = this->straight_path_initial_x = x; - this->paint_tile_initial_y = this->straight_path_initial_y = y; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + this->paint_tile_initial_x = this->straight_path_initial_x = pos.x(); + this->paint_tile_initial_y = this->straight_path_initial_y = pos.y(); emit mouseEvent(event, this); } @@ -48,25 +47,22 @@ void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { } else if (map) { Blockdata *oldCollision = map->layout->blockdata->copy(); - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); // Set straight paths on/off and snap to the dominant axis when on if (event->modifiers() & Qt::ControlModifier) { this->lockNondominantAxis(event); - x = this->adjustCoord(x, MapPixmapItem::Axis::X); - y = this->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; this->lockedAxis = MapPixmapItem::Axis::None; } - Block *block = map->getBlock(x, y); + Block *block = map->getBlock(pos.x(), pos.y()); if (block) { block->collision = this->movementPermissionsSelector->getSelectedCollision(); block->elevation = this->movementPermissionsSelector->getSelectedElevation(); - map->setBlock(x, y, *block, true); + map->setBlock(pos.x(), pos.y(), *block, true); } Blockdata *newCollision = map->layout->blockdata->copy(); @@ -85,12 +81,10 @@ void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { } else if (map) { Blockdata *oldCollision = map->layout->blockdata->copy(); - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); uint16_t collision = this->movementPermissionsSelector->getSelectedCollision(); uint16_t elevation = this->movementPermissionsSelector->getSelectedElevation(); - map->floodFillCollisionElevation(x, y, collision, elevation); + map->floodFillCollisionElevation(pos.x(), pos.y(), collision, elevation); Blockdata *newCollision = map->layout->blockdata->copy(); if (newCollision->equals(oldCollision)) { @@ -107,12 +101,10 @@ void CollisionPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { this->actionId_++; } else if (map) { Blockdata *oldCollision = map->layout->blockdata->copy(); - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); uint16_t collision = this->movementPermissionsSelector->getSelectedCollision(); uint16_t elevation = this->movementPermissionsSelector->getSelectedElevation(); - map->magicFillCollisionElevation(x, y, collision, elevation); + map->magicFillCollisionElevation(pos.x(), pos.y(), collision, elevation); Blockdata *newCollision = map->layout->blockdata->copy(); if (newCollision->equals(oldCollision)) { @@ -125,26 +117,22 @@ void CollisionPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { } void CollisionPixmapItem::pick(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + Block *block = map->getBlock(pos.x(), pos.y()); if (block) { this->movementPermissionsSelector->select(block->collision, block->elevation); } } void CollisionPixmapItem::updateMovementPermissionSelection(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); // Snap point to within map bounds. - if (x < 0) x = 0; - if (x >= map->getWidth()) x = map->getWidth() - 1; - if (y < 0) y = 0; - if (y >= map->getHeight()) y = map->getHeight() - 1; + if (pos.x() < 0) pos.setX(0); + if (pos.x() >= map->getWidth()) pos.setX(map->getWidth() - 1); + if (pos.y() < 0) pos.setY(0); + if (pos.y() >= map->getHeight()) pos.setY(map->getHeight() - 1); - Block *block = map->getBlock(x, y); + Block *block = map->getBlock(pos.x(), pos.y()); this->movementPermissionsSelector->select(block->collision, block->elevation); } diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index 1743ab50..cde101b1 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -1,8 +1,8 @@ #include "draggablepixmapitem.h" - #include "editor.h" - #include "editcommands.h" +#include "mapruler.h" +#include "metatile.h" static unsigned currentActionId = 0; @@ -58,8 +58,9 @@ void DraggablePixmapItem::bindToUserData(QComboBox *combo, QString key) { void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { active = true; - last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16; - last_y = static_cast(mouse->pos().y() + this->pos().y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(mouse->scenePos()); + last_x = pos.x(); + last_y = pos.y(); this->editor->selectMapEvent(this, mouse->modifiers() & Qt::ControlModifier); this->editor->selectingEvent = true; } @@ -73,10 +74,9 @@ void DraggablePixmapItem::move(int x, int y) { void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { if (active) { - int x = static_cast(mouse->pos().x() + this->pos().x()) / 16; - int y = static_cast(mouse->pos().y() + this->pos().y()) / 16; - emit this->editor->map_item->hoveredMapMetatileChanged(x, y); - if (x != last_x || y != last_y) { + QPoint pos = Metatile::coordFromPixmapCoord(mouse->scenePos()); + emit this->editor->map_item->hoveredMapMetatileChanged(pos); + if (pos.x() != last_x || pos.y() != last_y) { QList selectedEvents; if (editor->selected_events->contains(this)) { for (DraggablePixmapItem *item : *editor->selected_events) { @@ -85,9 +85,9 @@ void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { } else { selectedEvents.append(this->event); } - editor->map->editHistory.push(new EventMove(selectedEvents, x - last_x, y - last_y, currentActionId)); - last_x = x; - last_y = y; + editor->map->editHistory.push(new EventMove(selectedEvents, pos.x() - last_x, pos.y() - last_y, currentActionId)); + last_x = pos.x(); + last_y = pos.y(); } } } diff --git a/src/ui/mappixmapitem.cpp b/src/ui/mappixmapitem.cpp index 60555d84..0e02545d 100644 --- a/src/ui/mappixmapitem.cpp +++ b/src/ui/mappixmapitem.cpp @@ -1,4 +1,5 @@ #include "mappixmapitem.h" +#include "metatile.h" #include "log.h" #include "editcommands.h" @@ -10,15 +11,12 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); // Set straight paths on/off and snap to the dominant axis when on if (event->modifiers() & Qt::ControlModifier) { this->lockNondominantAxis(event); - x = this->adjustCoord(x, MapPixmapItem::Axis::X); - y = this->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; this->lockedAxis = MapPixmapItem::Axis::None; @@ -29,15 +27,15 @@ void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); if (settings->smartPathsEnabled) { if (!shiftPressed && selectionDimensions.x() == 3 && selectionDimensions.y() == 3) { - paintSmartPath(x, y); + paintSmartPath(pos.x(), pos.y()); } else { - paintNormal(x, y); + paintNormal(pos.x(), pos.y()); } } else { if (shiftPressed && selectionDimensions.x() == 3 && selectionDimensions.y() == 3) { - paintSmartPath(x, y); + paintSmartPath(pos.x(), pos.y()); } else { - paintNormal(x, y); + paintNormal(pos.x(), pos.y()); } } } @@ -49,29 +47,26 @@ void MapPixmapItem::shift(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); // Set straight paths on/off and snap to the dominant axis when on if (event->modifiers() & Qt::ControlModifier) { this->lockNondominantAxis(event); - x = this->adjustCoord(x, MapPixmapItem::Axis::X); - y = this->adjustCoord(y, MapPixmapItem::Axis::Y); + pos = this->adjustCoords(pos); } else { this->prevStraightPathState = false; this->lockedAxis = MapPixmapItem::Axis::None; } if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); selection.clear(); } else if (event->type() == QEvent::GraphicsSceneMouseMove) { - if (x != selection_origin.x() || y != selection_origin.y()) { - int xDelta = x - selection_origin.x(); - int yDelta = y - selection_origin.y(); + if (pos.x() != selection_origin.x() || pos.y() != selection_origin.y()) { + int xDelta = pos.x() - selection_origin.x(); + int yDelta = pos.y() - selection_origin.y(); this->shift(xDelta, yDelta); - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); selection.clear(); draw(); } @@ -286,18 +281,16 @@ void MapPixmapItem::lockNondominantAxis(QGraphicsSceneMouseEvent *event) { if (this->lockedAxis != MapPixmapItem::Axis::None || event->type() == QEvent::GraphicsSceneMouseRelease) return; - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (!this->prevStraightPathState) { this->prevStraightPathState = true; - this->straight_path_initial_x = x; - this->straight_path_initial_y = y; + this->straight_path_initial_x = pos.x(); + this->straight_path_initial_y = pos.y(); } // Only lock an axis when the current position != initial - int xDiff = x - this->straight_path_initial_x; - int yDiff = y - this->straight_path_initial_y; + int xDiff = pos.x() - this->straight_path_initial_x; + int yDiff = pos.y() - this->straight_path_initial_y; if (xDiff || yDiff) { if (abs(xDiff) < abs(yDiff)) { this->lockedAxis = MapPixmapItem::Axis::X; @@ -308,38 +301,36 @@ void MapPixmapItem::lockNondominantAxis(QGraphicsSceneMouseEvent *event) { } // Adjust the cooresponding coordinate when it is locked -int MapPixmapItem::adjustCoord(int coord, MapPixmapItem::Axis axis) { - if (axis == MapPixmapItem::Axis::X && this->lockedAxis == MapPixmapItem::Axis::X) { - coord = this->straight_path_initial_x; - } else if (axis == MapPixmapItem::Axis::Y && this->lockedAxis == MapPixmapItem::Axis::Y) { - coord = this->straight_path_initial_y; +QPoint MapPixmapItem::adjustCoords(QPoint pos) { + if (this->lockedAxis == MapPixmapItem::Axis::X) { + pos.setX(this->straight_path_initial_x); + } else if (this->lockedAxis == MapPixmapItem::Axis::Y) { + pos.setY(this->straight_path_initial_y); } - return coord; + return pos; } void MapPixmapItem::updateMetatileSelection(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); // Snap point to within map bounds. - if (x < 0) x = 0; - if (x >= map->getWidth()) x = map->getWidth() - 1; - if (y < 0) y = 0; - if (y >= map->getHeight()) y = map->getHeight() - 1; + if (pos.x() < 0) pos.setX(0); + if (pos.x() >= map->getWidth()) pos.setX(map->getWidth() - 1); + if (pos.y() < 0) pos.setY(0); + if (pos.y() >= map->getHeight()) pos.setY(map->getHeight() - 1); // Update/apply copied metatiles. if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); selection.clear(); - selection.append(QPoint(x, y)); - Block *block = map->getBlock(x, y); + selection.append(QPoint(pos.x(), pos.y())); + Block *block = map->getBlock(pos.x(), pos.y()); this->metatileSelector->selectFromMap(block->tile, block->collision, block->elevation); } else if (event->type() == QEvent::GraphicsSceneMouseMove) { int x1 = selection_origin.x(); int y1 = selection_origin.y(); - int x2 = x; - int y2 = y; + int x2 = pos.x(); + int y2 = pos.y(); if (x1 > x2) SWAP(x1, x2); if (y1 > y2) SWAP(y1, y2); selection.clear(); @@ -369,19 +360,17 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + Block *block = map->getBlock(pos.x(), pos.y()); QList *selectedMetatiles = this->metatileSelector->getSelectedMetatiles(); QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions(); int tile = selectedMetatiles->first(); if (selectedMetatiles->count() > 1 || (block && block->tile != tile)) { bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier; if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selectionDimensions.x() == 3 && selectionDimensions.y() == 3) - this->floodFillSmartPath(x, y); + this->floodFillSmartPath(pos.x(), pos.y()); else - this->floodFill(x, y); + this->floodFill(pos.x(), pos.y()); } } } @@ -392,10 +381,8 @@ void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) { if (event->type() == QEvent::GraphicsSceneMouseRelease) { actionId_++; } else { - QPointF pos = event->pos(); - int initialX = static_cast(pos.x()) / 16; - int initialY = static_cast(pos.y()) / 16; - this->magicFill(initialX, initialY); + QPoint initialPos = Metatile::coordFromPixmapCoord(event->pos()); + this->magicFill(initialPos.x(), initialPos.y()); } } } @@ -691,26 +678,22 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri } void MapPixmapItem::pick(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - Block *block = map->getBlock(x, y); + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + Block *block = map->getBlock(pos.x(), pos.y()); if (block) { this->metatileSelector->selectFromMap(block->tile, block->collision, block->elevation); } } void MapPixmapItem::select(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); if (event->type() == QEvent::GraphicsSceneMousePress) { - selection_origin = QPoint(x, y); + selection_origin = QPoint(pos.x(), pos.y()); selection.clear(); } else if (event->type() == QEvent::GraphicsSceneMouseMove) { if (event->buttons() & Qt::LeftButton) { selection.clear(); - selection.append(QPoint(x, y)); + selection.append(QPoint(pos.x(), pos.y())); } } else if (event->type() == QEvent::GraphicsSceneMouseRelease) { if (!selection.isEmpty()) { @@ -740,9 +723,8 @@ void MapPixmapItem::draw(bool ignoreCache) { } void MapPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { - int x = static_cast(event->pos().x()) / 16; - int y = static_cast(event->pos().y()) / 16; - emit this->hoveredMapMetatileChanged(x, y); + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + emit this->hoveredMapMetatileChanged(pos); if (this->settings->betterCursors && this->paintingMode != MapPixmapItem::PaintMode::Disabled) { setCursor(this->settings->mapCursor); } @@ -758,18 +740,15 @@ void MapPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { this->has_mouse = false; } void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QPointF pos = event->pos(); - int x = static_cast(pos.x()) / 16; - int y = static_cast(pos.y()) / 16; - this->paint_tile_initial_x = this->straight_path_initial_x = x; - this->paint_tile_initial_y = this->straight_path_initial_y = y; + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + this->paint_tile_initial_x = this->straight_path_initial_x = pos.x(); + this->paint_tile_initial_y = this->straight_path_initial_y = pos.y(); emit startPaint(event, this); emit mouseEvent(event, this); } void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - int x = static_cast(event->pos().x()) / 16; - int y = static_cast(event->pos().y()) / 16; - emit this->hoveredMapMetatileChanged(x, y); + QPoint pos = Metatile::coordFromPixmapCoord(event->pos()); + emit this->hoveredMapMetatileChanged(pos); emit mouseEvent(event, this); } void MapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { diff --git a/src/ui/mapruler.cpp b/src/ui/mapruler.cpp new file mode 100644 index 00000000..ed404176 --- /dev/null +++ b/src/ui/mapruler.cpp @@ -0,0 +1,189 @@ +#include "mapruler.h" +#include "metatile.h" + +#include +#include +#include +#include +#include + +int MapRuler::thickness = 3; + + +void MapRuler::init() { + setVisible(false); + setPoints(QPoint(), QPoint()); + anchored = false; + locked = false; + statusMessage = QString("Ruler: 0"); + xRuler = QRect(); + yRuler = QRect(); + cornerTick = QLine(); +} + +QRectF MapRuler::boundingRect() const { + return QRectF(-thickness, -thickness, pixWidth() + thickness * 2, pixHeight() + thickness * 2); +} + +QPainterPath MapRuler::shape() const { + QPainterPath ruler; + ruler.setFillRule(Qt::WindingFill); + ruler.addRect(xRuler); + ruler.addRect(yRuler); + ruler = ruler.simplified(); + for (int x = 17.5; x < pixWidth(); x += 16) + ruler.addRect(x, xRuler.y(), 0, thickness); + for (int y = 17.5; y < pixHeight(); y += 16) + ruler.addRect(yRuler.x(), y, thickness, 0); + return ruler; +} + +void MapRuler::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { + painter->setPen(QPen(borderColor)); + painter->setBrush(QBrush(innerColor)); + painter->drawPath(shape()); + if (deltaX() && deltaY()) + painter->drawLine(cornerTick); +} + +bool MapRuler::eventFilter(QObject*, QEvent *event) { + if (!isEnabled() || mapSize.isEmpty()) + return false; + + if (event->type() == QEvent::GraphicsSceneMousePress || event->type() == QEvent::GraphicsSceneMouseMove) { + auto mouse_event = static_cast(event); + if (mouse_event->button() == Qt::RightButton || anchored) { + mouseEvent(mouse_event); + event->accept(); + return true; + } + } + + return false; +} + +void MapRuler::mouseEvent(QGraphicsSceneMouseEvent *event) { + if (!anchored && event->button() == Qt::RightButton) { + setAnchor(event->scenePos(), event->screenPos()); + } else if (anchored) { + if (event->button() == Qt::LeftButton) + locked = !locked; + if (event->button() == Qt::RightButton) + endAnchor(); + else + setEndPos(event->scenePos(), event->screenPos()); + } +} + +void MapRuler::setMapDimensions(const QSize &size) { + mapSize = size; + init(); +} + +void MapRuler::setEnabled(bool enabled) { + QGraphicsItem::setEnabled(enabled); + if (!enabled && anchored) + endAnchor(); +} + +QPoint MapRuler::snapToWithinBounds(QPoint pos) const { + if (pos.x() < 0) + pos.setX(0); + if (pos.y() < 0) + pos.setY(0); + if (pos.x() >= mapSize.width()) + pos.setX(mapSize.width() - 1); + if (pos.y() >= mapSize.height()) + pos.setY(mapSize.height() - 1); + return pos; +} + +void MapRuler::setAnchor(const QPointF &scenePos, const QPoint &screenPos) { + QPoint pos = Metatile::coordFromPixmapCoord(scenePos); + pos = snapToWithinBounds(pos); + anchored = true; + locked = false; + setPoints(pos, pos); + updateGeometry(); + setVisible(true); + showDimensions(screenPos); +} + +void MapRuler::endAnchor() { + emit deactivated(endPos()); + hideDimensions(); + prepareGeometryChange(); + init(); +} + +void MapRuler::setEndPos(const QPointF &scenePos, const QPoint &screenPos) { + if (locked) + return; + QPoint pos = Metatile::coordFromPixmapCoord(scenePos); + pos = snapToWithinBounds(pos); + const QPoint lastEndPos = endPos(); + setP2(pos); + if (pos != lastEndPos) + updateGeometry(); + showDimensions(screenPos); +} + +void MapRuler::showDimensions(const QPoint &screenPos) const { + // This is a hack to make the tool tip follow the cursor since it won't change position if the text is the same. + QToolTip::showText(screenPos + QPoint(16, -8), statusMessage + ' '); + QToolTip::showText(screenPos + QPoint(16, -8), statusMessage); +} + +void MapRuler::hideDimensions() const { + QToolTip::hideText(); +} + +void MapRuler::updateGeometry() { + prepareGeometryChange(); + setPos(QPoint(left() * 16 + 7, top() * 16 + 7)); + /* Determine what quadrant the end point is in relative to the anchor point. The anchor + * point is the top-left corner of the metatile the ruler starts in, so a zero-length(s) + * ruler is considered to be in the bottom-right quadrant from the anchor point. */ + if (deltaX() < 0 && deltaY() < 0) { + // Top-left + xRuler = QRect(0, pixHeight(), pixWidth() + thickness, thickness); + yRuler = QRect(0, 0, thickness, pixHeight() + thickness); + cornerTick = QLineF(yRuler.x() + 0.5, xRuler.y() + thickness - 0.5, yRuler.x() + thickness, xRuler.y()); + statusMessage = QString("Ruler: Left %1, Up %2").arg(width()).arg(height()); + } else if (deltaX() < 0) { + // Bottom-left + xRuler = QRect(0, 0, pixWidth() + thickness, thickness); + yRuler = QRect(0, 0, thickness, pixHeight() + thickness); + cornerTick = QLineF(xRuler.x() + 0.5, xRuler.y() + 0.5, xRuler.x() + thickness, xRuler.y() + thickness); + statusMessage = QString("Ruler: Left %1").arg(width()); + if (deltaY()) + statusMessage += QString(", Down %1").arg(height()); + } else if (deltaY() < 0) { + // Top-right + xRuler = QRect(0, pixHeight(), pixWidth() + thickness, thickness); + yRuler = QRect(pixWidth(), 0, thickness, pixHeight() + thickness); + cornerTick = QLineF(yRuler.x(), xRuler.y(), yRuler.x() + thickness - 0.5, xRuler.y() + thickness - 0.5); + statusMessage = QString("Ruler: "); + if (deltaX()) + statusMessage += QString("Right %1, ").arg(width()); + statusMessage += QString("Up %1").arg(height()); + } else { + // Bottom-right + xRuler = QRect(0, 0, pixWidth() + thickness, thickness); + yRuler = QRect(pixWidth(), 0, thickness, pixHeight() + thickness); + cornerTick = QLineF(yRuler.x(), yRuler.y() + thickness, yRuler.x() + thickness - 0.5, yRuler.y() + 0.5); + statusMessage = QString("Ruler: "); + if (deltaX() || deltaY()) { + if (deltaX()) + statusMessage += QString("Right %1").arg(width()); + if (deltaY()) { + if (deltaX()) + statusMessage += ", "; + statusMessage += QString("Down: %1").arg(height()); + } + } else { + statusMessage += QString("0"); + } + } + emit lengthChanged(); +}