diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2021-01-12 15:20:05 +0100 |
---|---|---|
committer | bstoeger <32835590+bstoeger@users.noreply.github.com> | 2021-01-20 08:47:18 +0100 |
commit | 2b961414d7a52d442f5ecd8c2e42e43d044d0d5e (patch) | |
tree | b1d6c47b94dfc80481aaad2e3cfde35335646576 /stats/legend.cpp | |
parent | 9be23b5f3f8373ba115f4e66e77c0537abe982b2 (diff) | |
download | subsurface-2b961414d7a52d442f5ecd8c2e42e43d044d0d5e.tar.gz |
statistics: draw legend as a QSGNode
In order not to waste CPU by constantly rerendering the chart,
we must use these weird OpenGL QSGNode things. The interface
is appallingly low-level and unfriendly.
As a first test, try to convert the legend. Create a wrapper
class that represents a rectangular item with a texture
and that will certainly need some (lots of) optimization.
Make sure that all low-level QSG-objects are only accessed
in the rendering thread. This means that the wrapper has
to maintain a notion of "dirtiness" of the state. I.e.
which part of the QSG-objects have to be modified.
From the low-level wrapper derive a class that draws a rounded
rectangle for every resize. The child class of that must then
paint on the rectangle after every resize.
That looks all not very fortunate, but it displays a
legend and will make it possible to move the legend
without and drawing operations, only shifting around
an OpenGL surface.
The render thread goes through all chart-items and
rerenders them if dirty. Currently, on deletion
of these items, this list is not reset. I.e. currently
it is not supported to remove individual items.
Only the full scene can be cleared!
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'stats/legend.cpp')
-rw-r--r-- | stats/legend.cpp | 90 |
1 files changed, 34 insertions, 56 deletions
diff --git a/stats/legend.cpp b/stats/legend.cpp index 27607fb51..2cbd883db 100644 --- a/stats/legend.cpp +++ b/stats/legend.cpp @@ -4,8 +4,6 @@ #include "zvalues.h" #include <QFontMetrics> -#include <QGraphicsScene> -#include <QGraphicsSceneMouseEvent> #include <QPen> static const double legendBorderSize = 2.0; @@ -16,51 +14,30 @@ 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, QPen(legendBorderColor, legendBorderSize), QBrush(legendColor), legendBoxBorderRadius), + displayedItems(0), width(0.0), height(0.0), + font(QFont()) // Make configurable { - 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 +45,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 +77,32 @@ 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); - itemPos.rx() += fontHeight + 2.0 * legendBoxBorderSize; - entries[i].text->setPos(itemPos); - entries[i].rect->show(); - entries[i].text->show(); + painter->drawRect(rect); } - for (int i = displayedItems; i < (int)entries.size(); ++i) { - entries[i].rect->hide(); - entries[i].text->hide(); + + // 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; + QRectF rect(itemPos, QSizeF(entries[i].width, fontHeight)); + painter->drawText(rect, entries[i].name); } - show(); + + // For now, place the legend in the top right corner. + QPointF pos(size.width() - width - 10.0, 10.0); + setPos(pos); } |