diff --git a/include/editor.h b/include/editor.h index c9f7bcea..72b94f89 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; @@ -115,6 +116,7 @@ public: QList gridLines; MovableRect *playerViewRect = nullptr; CursorTileRect *cursorMapTileRect = nullptr; + MapRuler *map_ruler = nullptr; QGraphicsScene *scene_metatiles = nullptr; QGraphicsScene *scene_current_metatile_selection = nullptr; diff --git a/include/ui/mapruler.h b/include/ui/mapruler.h new file mode 100644 index 00000000..11adc535 --- /dev/null +++ b/include/ui/mapruler.h @@ -0,0 +1,71 @@ +#ifndef MAPRULER_H +#define MAPRULER_H + +#include +#include +#include + + +class MapRuler : public QGraphicsItem, private QLine +{ +public: + MapRuler(QColor interior = Qt::yellow, QColor exterior = Qt::black) + : interiorColor(interior), exteriorColor(exterior) + { + init(); + setAcceptedMouseButtons(Qt::RightButton); + } + void init(); + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override; + QPainterPath shape() const override; + + // Anchor the ruler on metatile 'tilePos' and show the ruler + void setAnchor(const QPoint &tilePos); + // Anchor the ruler on metatile (tileX,tileY) and show the ruler + void setAnchor(int tileX, int tileY) { setAnchor(QPoint(tileX, tileY)); } + // Release the ruler anchor and hide the ruler + void endAnchor(); + // Set the ruler end point to metatile 'tilePos' and repaint + void setEndPos(const QPoint &tilePos); + // Set the ruler end point to metatile (tileX, tileY) and repaint + void setEndPos(int tileX, int tileY) { setEndPos(QPoint(tileX, tileY)); } + + // 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 left edge in metatiles + int left() const { return qMin(anchor().x(), endPos().x()); } + // Y-coordinate of the ruler 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()); } + // Ruler width in map pixels + int pixWidth() const { return width() * 16; } + // Ruler height in map pixels + int pixHeight() const { return height() * 16; } + + bool mousePressed(Qt::MouseButtons buttons) { return buttons & acceptedMouseButtons(); } + +private: + QRect xRuler; + QRect yRuler; + QRect widthTextBox; + QRect heightTextBox; + QColor interiorColor; + QColor exteriorColor; + + static int padding; + static int thickness; + + void updateShape(); +}; + +#endif // MAPRULER_H diff --git a/porymap.pro b/porymap.pro index 04fb94e2..ff459399 100644 --- a/porymap.pro +++ b/porymap.pro @@ -68,6 +68,8 @@ SOURCES += src/core/block.cpp \ src/ui/regionmapeditor.cpp \ src/ui/newmappopup.cpp \ src/ui/mapimageexporter.cpp \ + src/ui/newtilesetdialog.cpp \ + src/ui/mapruler.cpp \ src/config.cpp \ src/editor.cpp \ src/main.cpp \ @@ -75,8 +77,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 \ @@ -134,15 +135,16 @@ 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/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/editor.cpp b/src/editor.cpp index 1345adb6..00f562fc 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -23,6 +23,7 @@ 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(); /// 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 +42,7 @@ Editor::~Editor() delete this->settings; delete this->playerViewRect; delete this->cursorMapTileRect; + delete this->map_ruler; closeProject(); } @@ -1119,7 +1121,14 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item } } } else if (obj_edit_mode == "select") { - // do nothing here, at least for now + if (!this->map_ruler->isEnabled() && this->map_ruler->mousePressed(event->buttons())) { + this->map_ruler->setAnchor(x, y); + } else if (event->type() == QEvent::GraphicsSceneMouseRelease + && !this->map_ruler->mousePressed(event->buttons())) { + this->map_ruler->endAnchor(); + } else if (this->map_ruler->isEnabled()) { + this->map_ruler->setEndPos(x, y); + } } else if (obj_edit_mode == "shift" && item->map) { static QPoint selection_origin; static unsigned actionId = 0; @@ -1217,6 +1226,7 @@ bool Editor::displayMap() { delete map_item; scene->removeItem(this->playerViewRect); scene->removeItem(this->cursorMapTileRect); + scene->removeItem(this->map_ruler); } displayMetatileSelector(); @@ -1233,8 +1243,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); diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index 1743ab50..3ae3cca4 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -1,8 +1,7 @@ #include "draggablepixmapitem.h" - #include "editor.h" - #include "editcommands.h" +#include "mapruler.h" static unsigned currentActionId = 0; @@ -57,9 +56,15 @@ void DraggablePixmapItem::bindToUserData(QComboBox *combo, QString key) { } void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { + int x = static_cast(mouse->pos().x() + this->pos().x()) / 16; + int y = static_cast(mouse->pos().y() + this->pos().y()) / 16; + if (mouse->buttons() & this->editor->map_ruler->acceptedMouseButtons()) { + this->editor->map_ruler->setAnchor(x, y); + return; + } active = true; - last_x = static_cast(mouse->pos().x() + this->pos().x()) / 16; - last_y = static_cast(mouse->pos().y() + this->pos().y()) / 16; + last_x = x; + last_y = y; this->editor->selectMapEvent(this, mouse->modifiers() & Qt::ControlModifier); this->editor->selectingEvent = true; } @@ -76,7 +81,9 @@ void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { 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) { + if (this->editor->map_ruler->isEnabled()) { + this->editor->map_ruler->setEndPos(x, y); + } else if (x != last_x || y != last_y) { QList selectedEvents; if (editor->selected_events->contains(this)) { for (DraggablePixmapItem *item : *editor->selected_events) { @@ -92,9 +99,14 @@ void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { } } -void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) { - active = false; - currentActionId++; +void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { + if (!(mouse->buttons() & this->editor->map_ruler->acceptedMouseButtons())) { + this->editor->map_ruler->endAnchor(); + } + if (!this->editor->map_ruler->isEnabled()) { + active = false; + currentActionId++; + } } void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { diff --git a/src/ui/mapruler.cpp b/src/ui/mapruler.cpp new file mode 100644 index 00000000..c1b74a25 --- /dev/null +++ b/src/ui/mapruler.cpp @@ -0,0 +1,94 @@ +#include "mapruler.h" + +#include +#include +#include + +int MapRuler::padding = 24; +int MapRuler::thickness = 3; + + +void MapRuler::init() { + setVisible(false); + setEnabled(false); + setPoints(QPoint(), QPoint()); + xRuler = QRect(); + yRuler = QRect(); + widthTextBox = QRect(); + heightTextBox = QRect(); +} + +QRectF MapRuler::boundingRect() const { + return QRectF(-padding, -padding, pixWidth() + padding * 2, pixHeight() + padding * 2); +} + +QPainterPath MapRuler::shape() const { + QPainterPath ruler; + ruler.setFillRule(Qt::WindingFill); + ruler.addRect(xRuler); + ruler.addRect(yRuler); + ruler = ruler.simplified(); + return ruler; +} + +void MapRuler::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { + painter->setPen(exteriorColor); + painter->setBrush(QBrush(interiorColor)); + painter->drawPath(shape()); + painter->drawText(widthTextBox, Qt::AlignCenter, QString("%1").arg(width())); + painter->drawText(heightTextBox, Qt::AlignCenter, QString("%1").arg(height())); +} + +void MapRuler::setAnchor(const QPoint &tilePos) { + setEnabled(true); + setPoints(tilePos, tilePos); + updateShape(); + setVisible(true); +} + +void MapRuler::endAnchor() { + setVisible(false); + setEnabled(false); + prepareGeometryChange(); + init(); +} + +void MapRuler::setEndPos(const QPoint &tilePos) { + if (!isEnabled() || (tilePos == endPos() && tilePos != anchor())) + return; + prepareGeometryChange(); + setP2(tilePos); + updateShape(); +} + +void MapRuler::updateShape() { + setPos(QPoint(left() * 16 + 7, top() * 16 + 7)); + /* Determines what quadrant the ruler exists 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 + * treated as being 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); + widthTextBox = QRect(0, pixHeight(), pixWidth(), padding); + heightTextBox = QRect(-padding, 0, padding, pixHeight()); + } else if (deltaX() < 0) { + // Bottom-left + xRuler = QRect(0, 0, pixWidth() + thickness, thickness); + yRuler = QRect(0, 0, thickness, pixHeight() + thickness); + widthTextBox = QRect(0, -padding, pixWidth(), padding); + heightTextBox = QRect(-padding, 0, padding, pixHeight()); + } else if (deltaY() < 0) { + // Top-right + xRuler = QRect(0, pixHeight(), pixWidth() + thickness, thickness); + yRuler = QRect(pixWidth(), 0, thickness, pixHeight() + thickness); + widthTextBox = QRect(0, pixHeight(), pixWidth(), padding); + heightTextBox = QRect(pixWidth(), 0, padding, pixHeight()); + } else { + // Bottom-right + xRuler = QRect(0, 0, pixWidth() + thickness, thickness); + yRuler = QRect(pixWidth(), 0, thickness, pixHeight() + thickness); + widthTextBox = QRect(0, -padding, pixWidth(), padding); + heightTextBox = QRect(pixWidth(), 0, padding, pixHeight()); + } +}