aboutsummaryrefslogtreecommitdiffstats
path: root/stats/legend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'stats/legend.cpp')
-rw-r--r--stats/legend.cpp124
1 files changed, 67 insertions, 57 deletions
diff --git a/stats/legend.cpp b/stats/legend.cpp
index 27607fb51..fc8656828 100644
--- a/stats/legend.cpp
+++ b/stats/legend.cpp
@@ -3,9 +3,8 @@
#include "statscolors.h"
#include "zvalues.h"
+#include <cmath>
#include <QFontMetrics>
-#include <QGraphicsScene>
-#include <QGraphicsSceneMouseEvent>
#include <QPen>
static const double legendBorderSize = 2.0;
@@ -13,54 +12,33 @@ static const double legendBoxBorderSize = 1.0;
static const double legendBoxBorderRadius = 4.0; // radius of rounded corners
static const double legendBoxScale = 0.8; // 1.0: text-height of the used font
static const double legendInternalBorderSize = 2.0;
-static const QColor legendColor(0x00, 0x8e, 0xcc, 192); // Note: fourth argument is opacity
-static const QColor legendBorderColor(Qt::black);
-Legend::Legend(const std::vector<QString> &names) :
- RoundRectItem(legendBoxBorderRadius),
- displayedItems(0), width(0.0), height(0.0)
+Legend::Legend(StatsView &view, const std::vector<QString> &names) :
+ ChartRectItem(view, ChartZValue::Legend,
+ QPen(legendBorderColor, legendBorderSize), QBrush(legendColor), legendBoxBorderRadius),
+ displayedItems(0), width(0.0), height(0.0),
+ font(QFont()), // Make configurable
+ posInitialized(false)
{
- setZValue(ZValues::legend);
entries.reserve(names.size());
+ QFontMetrics fm(font);
+ fontHeight = fm.height();
int idx = 0;
for (const QString &name: names)
- entries.emplace_back(name, idx++, (int)names.size(), this);
-
- // Calculate the height and width of the elements
- if (!entries.empty()) {
- QFontMetrics fm(entries[0].text->font());
- fontHeight = fm.height();
- for (Entry &e: entries)
- e.width = fontHeight + 2.0 * legendBoxBorderSize +
- fm.size(Qt::TextSingleLine, e.text->text()).width();
- } else {
- // Set to an arbitrary non-zero value, because Coverity doesn't understand
- // that we don't use the value as divisor below if entries is empty.
- fontHeight = 10.0;
- }
- setPen(QPen(legendBorderColor, legendBorderSize));
- setBrush(QBrush(legendColor));
+ entries.emplace_back(name, idx++, (int)names.size(), fm);
}
-Legend::Entry::Entry(const QString &name, int idx, int numBins, QGraphicsItem *parent) :
- rect(new QGraphicsRectItem(parent)),
- text(new QGraphicsSimpleTextItem(name, parent)),
- width(0)
+Legend::Entry::Entry(const QString &name, int idx, int numBins, const QFontMetrics &fm) :
+ name(name),
+ rectBrush(QBrush(binColor(idx, numBins)))
{
- rect->setZValue(ZValues::legend);
- rect->setPen(QPen(legendBorderColor, legendBoxBorderSize));
- rect->setBrush(QBrush(binColor(idx, numBins)));
- text->setZValue(ZValues::legend);
- text->setBrush(QBrush(darkLabelColor));
+ width = fm.height() + 2.0 * legendBoxBorderSize + fm.size(Qt::TextSingleLine, name).width();
}
void Legend::hide()
{
- for (Entry &e: entries) {
- e.rect->hide();
- e.text->hide();
- }
- QGraphicsRectItem::hide();
+ ChartRectItem::resize(QSizeF(1,1));
+ img->fill(Qt::transparent);
}
void Legend::resize()
@@ -68,7 +46,7 @@ void Legend::resize()
if (entries.empty())
return hide();
- QSizeF size = scene()->sceneRect().size();
+ QSizeF size = sceneSize();
// Silly heuristics: make the legend at most half as high and half as wide as the chart.
// Not sure if that makes sense - this might need some optimization.
@@ -100,31 +78,63 @@ void Legend::resize()
}
width += legendInternalBorderSize;
height = 2 * legendInternalBorderSize + numRows * fontHeight;
- updatePosition();
-}
-void Legend::updatePosition()
-{
- if (displayedItems <= 0)
- return hide();
- // For now, place the legend in the top right corner.
- QPointF pos(scene()->sceneRect().width() - width - 10.0, 10.0);
- setRect(QRectF(pos, QSizeF(width, height)));
+ ChartRectItem::resize(QSizeF(width, height));
+
+ // Paint rectangles
+ painter->setPen(QPen(legendBorderColor, legendBoxBorderSize));
for (int i = 0; i < displayedItems; ++i) {
- QPointF itemPos = pos + entries[i].pos;
+ QPointF itemPos = entries[i].pos;
+ painter->setBrush(entries[i].rectBrush);
QRectF rect(itemPos, QSizeF(fontHeight, fontHeight));
// Decrease box size by legendBoxScale factor
double delta = fontHeight * (1.0 - legendBoxScale) / 2.0;
rect = rect.adjusted(delta, delta, -delta, -delta);
- entries[i].rect->setRect(rect);
+ painter->drawRect(rect);
+ }
+
+ // Paint labels
+ painter->setPen(darkLabelColor); // QPainter uses pen not brush for text!
+ painter->setFont(font);
+ for (int i = 0; i < displayedItems; ++i) {
+ QPointF itemPos = entries[i].pos;
itemPos.rx() += fontHeight + 2.0 * legendBoxBorderSize;
- entries[i].text->setPos(itemPos);
- entries[i].rect->show();
- entries[i].text->show();
+ QRectF rect(itemPos, QSizeF(entries[i].width, fontHeight));
+ painter->drawText(rect, entries[i].name);
}
- for (int i = displayedItems; i < (int)entries.size(); ++i) {
- entries[i].rect->hide();
- entries[i].text->hide();
+
+ if (!posInitialized) {
+ // At first, place in top right corner
+ setPos(QPointF(size.width() - width - 10.0, 10.0));
+ posInitialized = true;
+ } else {
+ // Try to keep relative position with what it was before
+ setPos(QPointF(size.width() * centerPos.x() - width / 2.0,
+ size.height() * centerPos.y() - height / 2.0));
}
- show();
+}
+
+void Legend::setPos(QPointF newPos)
+{
+ // Round the position to integers or horrible artifacts appear (at least on desktop)
+ QPointF posInt(round(newPos.x()), round(newPos.y()));
+
+ // Make sure that the center is inside the drawing area,
+ // so that the user can't lose the legend.
+ QSizeF size = sceneSize();
+ if (size.width() < 1.0 || size.height() < 1.0)
+ return;
+ double widthHalf = floor(width / 2.0);
+ double heightHalf = floor(height / 2.0);
+ QPointF sanitizedPos(std::clamp(posInt.x(), -widthHalf, size.width() - widthHalf - 1.0),
+ std::clamp(posInt.y(), -heightHalf, size.height() - heightHalf - 1.0));
+
+ // Set position
+ ChartRectItem::setPos(sanitizedPos);
+
+ // Remember relative position of center for next time
+ QPointF centerPosAbsolute(sanitizedPos.x() + width / 2.0,
+ sanitizedPos.y() + height / 2.0);
+ centerPos = QPointF(centerPosAbsolute.x() / size.width(),
+ centerPosAbsolute.y() / size.height());
}