porymap/src/ui/mapruler.cpp

207 lines
7.4 KiB
C++
Raw Normal View History

#include "mapruler.h"
#include "metatile.h"
2020-10-24 12:45:08 +01:00
#include <QGraphicsSceneEvent>
#include <QPainter>
#include <QColor>
#include <QVector>
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)
2020-09-25 16:56:02 +01:00
ruler.addRect(x, xRuler.y(), 0, thickness);
for (int y = 16; y < pixHeight(); y += 16)
2020-09-25 16:56:02 +01:00
ruler.addRect(yRuler.x(), y, thickness, 0);
if (deltaX() && deltaY())
ruler.addPolygon(QVector<QPointF>({ 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) {
2020-10-24 12:45:08 +01:00
if (!isEnabled() || mapSize.isEmpty())
return false;
if (event->type() == QEvent::GraphicsSceneMousePress || event->type() == QEvent::GraphicsSceneMouseMove) {
auto *mouse_event = static_cast<QGraphicsSceneMouseEvent *>(event);
2020-10-24 12:45:08 +01:00
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());
2020-10-24 12:45:08 +01:00
} else if (anchored) {
if (event->button() == Qt::LeftButton)
locked = !locked;
if (event->button() == Qt::RightButton)
reset();
2020-10-24 12:45:08 +01:00
else
setEndPos(event->scenePos());
2020-10-24 12:45:08 +01:00
}
}
void MapRuler::setMapDimensions(const QSize &size) {
mapSize = size;
reset();
2020-10-24 12:45:08 +01:00
}
void MapRuler::reset() {
prepareGeometryChange();
hide();
setPoints(QPoint(), QPoint());
xRuler = QRectF();
yRuler = QRectF();
cornerTick = QLineF();
anchored = false;
locked = false;
emit statusChanged(QString());
2020-10-24 12:45:08 +01:00
}
void MapRuler::setAnchor(const QPointF &scenePos) {
2020-10-24 12:45:08 +01:00
QPoint pos = Metatile::coordFromPixmapCoord(scenePos);
pos = snapToWithinBounds(pos);
anchored = true;
locked = false;
2020-10-24 12:45:08 +01:00
setPoints(pos, pos);
updateGeometry();
show();
}
void MapRuler::setEndPos(const QPointF &scenePos) {
if (locked)
return;
QPoint pos = Metatile::coordFromPixmapCoord(scenePos);
2020-10-24 12:45:08 +01:00
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);
2020-09-25 16:56:02 +01:00
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);
2020-09-25 16:56:02 +01:00
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);
}