#include "mapruler.h" #include "metatile.h" #include #include #include #include MapRuler::MapRuler(int thickness, QColor innerColor, QColor borderColor) : /* The logical representation of rectangles are always one less than * the rendered shape, so we subtract 1 from thickness. */ thickness(thickness - 1), half_thickness(qreal(thickness - 1) / 2.0), innerColor(innerColor), borderColor(borderColor), mapSize(QSize()), xRuler(QRectF()), yRuler(QRectF()), cornerTick(QLineF()), anchored(false), locked(false) { connect(this, &QGraphicsObject::enabledChanged, [this]() { if (!isEnabled() && anchored) reset(); }); } QRectF MapRuler::boundingRect() const { return QRectF(-(half_thickness + 1), -(half_thickness + 1), 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 = 16; x < pixWidth(); x += 16) ruler.addRect(x, xRuler.y(), 0, thickness); for (int y = 16; y < pixHeight(); y += 16) ruler.addRect(yRuler.x(), y, thickness, 0); if (deltaX() && deltaY()) ruler.addPolygon(QVector({ cornerTick.p1(), cornerTick.p2() })); return ruler; } void MapRuler::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { painter->setPen(QPen(borderColor)); painter->setBrush(QBrush(innerColor)); painter->drawPath(shape()); } 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); } } return false; } void MapRuler::mouseEvent(QGraphicsSceneMouseEvent *event) { if (!anchored && event->button() == Qt::RightButton) { setAnchor(event->scenePos()); } else if (anchored) { if (event->button() == Qt::LeftButton) locked = !locked; if (event->button() == Qt::RightButton) reset(); else setEndPos(event->scenePos()); } } void MapRuler::setMapDimensions(const QSize &size) { mapSize = size; reset(); } void MapRuler::reset() { prepareGeometryChange(); hide(); setPoints(QPoint(), QPoint()); xRuler = QRectF(); yRuler = QRectF(); cornerTick = QLineF(); anchored = false; locked = false; emit statusChanged(QString()); } void MapRuler::setAnchor(const QPointF &scenePos) { QPoint pos = Metatile::coordFromPixmapCoord(scenePos); pos = snapToWithinBounds(pos); anchored = true; locked = false; setPoints(pos, pos); updateGeometry(); show(); } void MapRuler::setEndPos(const QPointF &scenePos) { if (locked) return; QPoint pos = Metatile::coordFromPixmapCoord(scenePos); pos = snapToWithinBounds(pos); const QPoint lastEndPos = endPos(); setP2(pos); if (pos != lastEndPos) updateGeometry(); } 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::updateGeometry() { prepareGeometryChange(); setPos(QPoint(left() * 16 + 8, top() * 16 + 8)); /* 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 * ruler is considered to be in the bottom-right quadrant from the anchor point. */ if (deltaX() < 0 && deltaY() < 0) { // Top-left xRuler = QRectF(-half_thickness, pixHeight() - half_thickness, pixWidth() + thickness, thickness); yRuler = QRectF(-half_thickness, -half_thickness, thickness, pixHeight() + thickness); cornerTick = QLineF(yRuler.x() + 0.5, xRuler.y() + thickness - 0.5, yRuler.x() + thickness, xRuler.y()); updateStatus(Qt::TopLeftCorner); } else if (deltaX() < 0) { // Bottom-left xRuler = QRectF(-half_thickness, -half_thickness, pixWidth() + thickness, thickness); yRuler = QRectF(-half_thickness, -half_thickness, thickness, pixHeight() + thickness); cornerTick = QLineF(xRuler.x() + 0.5, xRuler.y() + 0.5, xRuler.x() + thickness, xRuler.y() + thickness); updateStatus(Qt::BottomLeftCorner); } else if (deltaY() < 0) { // Top-right xRuler = QRectF(-half_thickness, pixHeight() - half_thickness, pixWidth() + thickness, thickness); yRuler = QRectF(pixWidth() - half_thickness, -half_thickness, thickness, pixHeight() + thickness); cornerTick = QLineF(yRuler.x(), xRuler.y(), yRuler.x() + thickness - 0.5, xRuler.y() + thickness - 0.5); updateStatus(Qt::TopRightCorner); } else { // Bottom-right xRuler = QRectF(-half_thickness, -half_thickness, pixWidth() + thickness, thickness); yRuler = QRectF(pixWidth() - half_thickness, -half_thickness, thickness, pixHeight() + thickness); cornerTick = QLineF(yRuler.x(), yRuler.y() + thickness, yRuler.x() + thickness - 0.5, yRuler.y() + 0.5); updateStatus(Qt::BottomRightCorner); } } void MapRuler::updateStatus(Qt::Corner corner) { QString statusMessage; switch (corner) { case Qt::TopLeftCorner: statusMessage = QString("Ruler: Left %1, Up %2\nStart(%3, %4), End(%5, %6)").arg(width()).arg(height()) .arg(anchor().x()).arg(anchor().y()).arg(endPos().x()).arg(endPos().y()); break; case Qt::BottomLeftCorner: statusMessage = QString("Ruler: Left %1").arg(width()); if (deltaY()) statusMessage += QString(", Down %1").arg(height()); statusMessage += QString("\nStart(%1, %2), End(%3, %4)") .arg(anchor().x()).arg(anchor().y()).arg(endPos().x()).arg(endPos().y()); break; case Qt::TopRightCorner: statusMessage = QString("Ruler: "); if (deltaX()) statusMessage += QString("Right %1, ").arg(width()); statusMessage += QString("Up %1\nStart(%2, %3), End(%4, %5)").arg(height()) .arg(anchor().x()).arg(anchor().y()).arg(endPos().x()).arg(endPos().y()); break; case Qt::BottomRightCorner: 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()); } statusMessage += QString("\nStart(%1, %2), End(%3, %4)") .arg(anchor().x()).arg(anchor().y()).arg(endPos().x()).arg(endPos().y()); } else { statusMessage += QString("0\nStart(%1, %2)") .arg(anchor().x()).arg(anchor().y()); } break; } emit statusChanged(statusMessage); }