stats: break out common QtQuick part of the code

Move most of the QtQuick code to its own directory, so that it
can be reused in the future for the chart.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2023-04-16 20:05:36 +02:00
parent 533cff96f3
commit 2eebae13dd
34 changed files with 1031 additions and 915 deletions

View file

@ -3,6 +3,7 @@
#include "statscolors.h"
#include "statsview.h"
#include "core/globals.h"
#include "qt-quick/chartitemhelper.h"
#include <cmath>
#include <QQuickWindow>
@ -14,104 +15,10 @@
static int selectionOverlayPixelSize = 2;
static int round_up(double f)
{
return static_cast<int>(ceil(f));
}
ChartItem::ChartItem(StatsView &v, size_t z) :
dirty(false), prev(nullptr), next(nullptr),
zValue(z), view(v)
{
// Register before the derived constructors run, so that the
// derived classes can mark the item as dirty in the constructor.
v.registerChartItem(*this);
}
ChartItem::~ChartItem()
{
}
QSizeF ChartItem::sceneSize() const
{
return view.size();
}
void ChartItem::markDirty()
{
view.registerDirtyChartItem(*this);
}
ChartPixmapItem::ChartPixmapItem(StatsView &v, size_t z) : HideableChartItem(v, z),
positionDirty(false), textureDirty(false)
{
}
ChartPixmapItem::~ChartPixmapItem()
{
painter.reset(); // Make sure to destroy painter before image that is painted on
}
void ChartPixmapItem::setTextureDirty()
{
textureDirty = true;
markDirty();
}
void ChartPixmapItem::setPositionDirty()
{
positionDirty = true;
markDirty();
}
void ChartPixmapItem::render()
{
if (!node) {
createNode(view.w()->createImageNode());
view.addQSGNode(node.get(), zValue);
}
updateVisible();
if (!img) {
resize(QSizeF(1,1));
img->fill(Qt::transparent);
}
if (textureDirty) {
texture.reset(view.w()->createTextureFromImage(*img, QQuickWindow::TextureHasAlphaChannel));
node->node->setTexture(texture.get());
textureDirty = false;
}
if (positionDirty) {
node->node->setRect(rect);
positionDirty = false;
}
}
void ChartPixmapItem::resize(QSizeF size)
{
painter.reset();
img.reset(new QImage(round_up(size.width()), round_up(size.height()), QImage::Format_ARGB32));
painter.reset(new QPainter(img.get()));
painter->setRenderHint(QPainter::Antialiasing);
rect.setSize(size);
setTextureDirty();
}
void ChartPixmapItem::setPos(QPointF pos)
{
rect.moveTopLeft(pos);
setPositionDirty();
}
QRectF ChartPixmapItem::getRect() const
{
return rect;
}
static const int scatterItemDiameter = 10;
static const int scatterItemBorder = 1;
ChartScatterItem::ChartScatterItem(StatsView &v, size_t z, const StatsTheme &theme, bool selected) : HideableChartItem(v, z),
ChartScatterItem::ChartScatterItem(ChartView &v, size_t z, const StatsTheme &theme, bool selected) : HideableChartItem(v, z),
theme(theme),
positionDirty(false), textureDirty(false),
highlight(selected ? Highlight::Selected : Highlight::Unselected)
@ -123,7 +30,7 @@ ChartScatterItem::~ChartScatterItem()
{
}
static QSGTexture *createScatterTexture(StatsView &view, const QColor &color, const QColor &borderColor)
static QSGTexture *createScatterTexture(ChartView &view, const QColor &color, const QColor &borderColor)
{
QImage img(scatterItemDiameter, scatterItemDiameter, QImage::Format_ARGB32);
img.fill(Qt::transparent);
@ -214,73 +121,7 @@ QRectF ChartScatterItem::getRect() const
return rect;
}
ChartRectItem::ChartRectItem(StatsView &v, size_t z,
const QPen &pen, const QBrush &brush, double radius) : ChartPixmapItem(v, z),
pen(pen), brush(brush), radius(radius)
{
}
ChartRectItem::~ChartRectItem()
{
}
void ChartRectItem::resize(QSizeF size)
{
ChartPixmapItem::resize(size);
img->fill(Qt::transparent);
painter->setPen(pen);
painter->setBrush(brush);
QSize imgSize = img->size();
int width = pen.width();
QRect rect(width / 2, width / 2, imgSize.width() - width, imgSize.height() - width);
painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
}
ChartTextItem::ChartTextItem(StatsView &v, size_t z, const QFont &f, const std::vector<QString> &text, bool center) :
ChartPixmapItem(v, z), f(f), center(center)
{
QFontMetrics fm(f);
double totalWidth = 1.0;
fontHeight = static_cast<double>(fm.height());
double totalHeight = std::max(1.0, static_cast<double>(text.size()) * fontHeight);
items.reserve(text.size());
for (const QString &s: text) {
double w = fm.size(Qt::TextSingleLine, s).width();
items.push_back({ s, w });
if (w > totalWidth)
totalWidth = w;
}
resize(QSizeF(totalWidth, totalHeight));
}
ChartTextItem::ChartTextItem(StatsView &v, size_t z, const QFont &f, const QString &text) :
ChartTextItem(v, z, f, std::vector<QString>({ text }), true)
{
}
void ChartTextItem::setColor(const QColor &c)
{
setColor(c, Qt::transparent);
}
void ChartTextItem::setColor(const QColor &c, const QColor &background)
{
img->fill(background);
double y = 0.0;
painter->setPen(QPen(c));
painter->setFont(f);
double totalWidth = getRect().width();
for (const auto &[s, w]: items) {
double x = center ? round((totalWidth - w) / 2.0) : 0.0;
QRectF rect(x, y, w, fontHeight);
painter->drawText(rect, s);
y += fontHeight;
}
setTextureDirty();
}
ChartPieItem::ChartPieItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) : ChartPixmapItem(v, z),
ChartPieItem::ChartPieItem(ChartView &v, size_t z, const StatsTheme &theme, double borderWidth) : ChartPixmapItem(v, z),
theme(theme),
borderWidth(borderWidth)
{
@ -322,101 +163,12 @@ void ChartPieItem::resize(QSizeF size)
img->fill(Qt::transparent);
}
ChartLineItemBase::ChartLineItemBase(StatsView &v, size_t z, QColor color, double width) : HideableChartItem(v, z),
ChartLineItemBase::ChartLineItemBase(ChartView &v, size_t z, QColor color, double width) : HideableChartItem(v, z),
color(color), width(width), positionDirty(false), materialDirty(false)
{
}
ChartLineItemBase::~ChartLineItemBase()
{
}
void ChartLineItemBase::setLine(QPointF fromIn, QPointF toIn)
{
from = fromIn;
to = toIn;
positionDirty = true;
markDirty();
}
// Helper function to set points
void setPoint(QSGGeometry::Point2D &v, const QPointF &p)
{
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()));
}
void setPoint(QSGGeometry::TexturedPoint2D &v, const QPointF &p, const QPointF &t)
{
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()),
static_cast<float>(t.x()), static_cast<float>(t.y()));
}
void ChartLineItem::render()
{
if (!node) {
geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2));
geometry->setDrawingMode(QSGGeometry::DrawLines);
material.reset(new QSGFlatColorMaterial);
createNode();
node->setGeometry(geometry.get());
node->setMaterial(material.get());
view.addQSGNode(node.get(), zValue);
positionDirty = materialDirty = true;
}
updateVisible();
if (positionDirty) {
// Attention: width is a geometry property and therefore handled by position dirty!
geometry->setLineWidth(static_cast<float>(width));
auto vertices = geometry->vertexDataAsPoint2D();
setPoint(vertices[0], from);
setPoint(vertices[1], to);
node->markDirty(QSGNode::DirtyGeometry);
}
if (materialDirty) {
material->setColor(color);
node->markDirty(QSGNode::DirtyMaterial);
}
positionDirty = materialDirty = false;
}
void ChartRectLineItem::render()
{
if (!node) {
geometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 5));
geometry->setDrawingMode(QSGGeometry::DrawLineStrip);
material.reset(new QSGFlatColorMaterial);
createNode();
node->setGeometry(geometry.get());
node->setMaterial(material.get());
view.addQSGNode(node.get(), zValue);
positionDirty = materialDirty = true;
}
updateVisible();
if (positionDirty) {
// Attention: width is a geometry property and therefore handled by position dirty!
geometry->setLineWidth(static_cast<float>(width));
auto vertices = geometry->vertexDataAsPoint2D();
setPoint(vertices[0], from);
setPoint(vertices[1], QPointF(from.x(), to.y()));
setPoint(vertices[2], to);
setPoint(vertices[3], QPointF(to.x(), from.y()));
setPoint(vertices[4], from);
node->markDirty(QSGNode::DirtyGeometry);
}
if (materialDirty) {
material->setColor(color);
node->markDirty(QSGNode::DirtyMaterial);
}
positionDirty = materialDirty = false;
}
ChartBarItem::ChartBarItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) : HideableChartItem(v, z),
ChartBarItem::ChartBarItem(ChartView &v, size_t z, const StatsTheme &theme, double borderWidth) : HideableChartItem(v, z),
theme(theme),
borderWidth(borderWidth), selected(false),
positionDirty(false), colorDirty(false), selectedDirty(false)
@ -548,7 +300,7 @@ QRectF ChartBarItem::getRect() const
return rect;
}
ChartBoxItem::ChartBoxItem(StatsView &v, size_t z, const StatsTheme &theme, double borderWidth) :
ChartBoxItem::ChartBoxItem(ChartView &v, size_t z, const StatsTheme &theme, double borderWidth) :
ChartBarItem(v, z, theme, borderWidth)
{
}