mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-17 12:37:46 -07:00
[VDE] Deck Analytics Widgets overhaul (#6463)
* [VDE] Deck Analytics Widgets overhaul Took 2 minutes Took 3 minutes Took 3 minutes * Qt5 version guards. Took 33 minutes Took 3 seconds * Include QtMath Took 3 minutes Took 8 seconds * Use getCards() Took 4 minutes * Non pointer stuff Took 52 seconds * Add a newline to the tooltip Took 2 minutes Took 27 seconds * Fix build failure on macOS 15 * Rename some things. Took 17 minutes Took 11 seconds Took 18 seconds * Address overloads, fix default configuration. Took 1 hour 9 minutes Took 8 seconds * Fix mana curve default config. Took 4 minutes * Namespace to Qt libs Took 5 minutes * Selection overlay is transparent for mouse events. Took 2 minutes * Brace initialize. Took 8 minutes * Debian 11. Took 5 minutes --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de> Co-authored-by: RickyRister <ricky.rister.wang@gmail.com>
This commit is contained in:
parent
36d8280765
commit
df9a8b2272
81 changed files with 4372 additions and 394 deletions
|
|
@ -0,0 +1,43 @@
|
|||
#include "bar_chart_background_widget.h"
|
||||
|
||||
BarChartBackgroundWidget::BarChartBackgroundWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
QSize BarChartBackgroundWidget::sizeHint() const
|
||||
{
|
||||
return QSize(100, 150);
|
||||
}
|
||||
|
||||
void BarChartBackgroundWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
constexpr int PAD = 4;
|
||||
constexpr int LABEL_H = 20;
|
||||
|
||||
int left = 46; // axis space + internal padding
|
||||
int right = width() - PAD;
|
||||
int top = PAD;
|
||||
int bottom = height() - PAD - LABEL_H;
|
||||
|
||||
int barAreaHeight = bottom - top;
|
||||
int barAreaWidth = right - left;
|
||||
|
||||
p.fillRect(QRect(left, top, barAreaWidth, barAreaHeight), QColor(250, 250, 250));
|
||||
|
||||
int ticks = 5;
|
||||
for (int i = 0; i <= ticks; i++) {
|
||||
float r = float(i) / ticks;
|
||||
int y = bottom - r * barAreaHeight;
|
||||
|
||||
p.setPen(QPen(QColor(180, 180, 180, 120), 1));
|
||||
p.drawLine(left, y, right, y);
|
||||
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(left - 35, y - 6, 32, 12, Qt::AlignRight | Qt::AlignVCenter, QString::number(int(r * highest)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef COCKATRICE_BAR_CHART_BACKGROUND_WIDGET_H
|
||||
#define COCKATRICE_BAR_CHART_BACKGROUND_WIDGET_H
|
||||
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
|
||||
class BarChartBackgroundWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
int highest = 0; // global maximum (shared across bars)
|
||||
int barCount = 0; // number of CMC columns
|
||||
int labelHeight = 20; // reserved for CMC numbers
|
||||
|
||||
explicit BarChartBackgroundWidget(QWidget *parent);
|
||||
public slots:
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_BAR_CHART_BACKGROUND_WIDGET_H
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
#include "bar_chart_widget.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QToolTip>
|
||||
|
||||
BarChartWidget::BarChartWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void BarChartWidget::setBars(const QVector<BarData> &newBars)
|
||||
{
|
||||
bars = newBars;
|
||||
update();
|
||||
}
|
||||
|
||||
void BarChartWidget::setHighest(int h)
|
||||
{
|
||||
highest = qMax(1, h);
|
||||
update();
|
||||
}
|
||||
|
||||
QSize BarChartWidget::sizeHint() const
|
||||
{
|
||||
return QSize(300, 200);
|
||||
}
|
||||
|
||||
QSize BarChartWidget::minimumSizeHint() const
|
||||
{
|
||||
return QSize(300, 50);
|
||||
}
|
||||
|
||||
void BarChartWidget::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
constexpr int PAD = 4;
|
||||
constexpr int LABEL_H = 20;
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
|
||||
int left = 46;
|
||||
int right = w - PAD;
|
||||
int top = PAD;
|
||||
int bottom = h - PAD - LABEL_H;
|
||||
|
||||
int barAreaHeight = bottom - top;
|
||||
int barAreaWidth = right - left;
|
||||
|
||||
int barCount = bars.size();
|
||||
if (barCount == 0)
|
||||
return;
|
||||
|
||||
int spacing = 6;
|
||||
int barWidth = (barAreaWidth - (barCount - 1) * spacing) / barCount;
|
||||
|
||||
// background
|
||||
p.fillRect(QRect(left, top, barAreaWidth, barAreaHeight), QColor(250, 250, 250));
|
||||
|
||||
// y-axis ticks
|
||||
int ticks = 5;
|
||||
// qInfo() << "Tick Positions ";
|
||||
for (int i = 0; i <= ticks; i++) {
|
||||
float r = float(i) / ticks;
|
||||
int tickVal = i * highest / ticks; // integer value of tick
|
||||
int y = bottom - (tickVal * barAreaHeight / highest);
|
||||
|
||||
// qInfo() << "Tick" << i << "value" << int(r * highest) << "y" << y;
|
||||
|
||||
p.setPen(QPen(QColor(180, 180, 180, 120), 1));
|
||||
p.drawLine(left, y, right, y);
|
||||
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(left - 35, y - 6, 32, 12, Qt::AlignRight | Qt::AlignVCenter, QString::number(int(r * highest)));
|
||||
}
|
||||
|
||||
// draw bars
|
||||
// qInfo() << "Bar Segments";
|
||||
int drawWidth = barWidth / 4; // 1/4 of allocated width
|
||||
int xOffset = (barWidth - drawWidth) / 2; // center the narrow bar
|
||||
|
||||
for (int i = 0; i < barCount; i++) {
|
||||
const BarData &bar = bars[i];
|
||||
int x = left + i * (barWidth + spacing) + xOffset; // shift to center
|
||||
int yCurrent = bottom;
|
||||
|
||||
for (int j = 0; j < bar.segments.size(); j++) {
|
||||
const auto &seg = bar.segments[j];
|
||||
int segHeight = (seg.value * barAreaHeight / highest);
|
||||
if (segHeight < 2 && seg.value > 0)
|
||||
segHeight = 2;
|
||||
|
||||
int topY = yCurrent - segHeight;
|
||||
|
||||
QRect r(x, topY, drawWidth, segHeight); // use drawWidth instead of barWidth
|
||||
bool isTop = (j == bar.segments.size() - 1);
|
||||
|
||||
QLinearGradient g(r.topLeft(), r.bottomLeft());
|
||||
g.setColorAt(0, seg.color.lighter(120));
|
||||
g.setColorAt(1, seg.color.darker(110));
|
||||
p.setBrush(g);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
if (isTop) {
|
||||
QPainterPath path;
|
||||
int radius = 6;
|
||||
|
||||
int bx = r.x();
|
||||
int by = r.y();
|
||||
int bw = r.width();
|
||||
int bh = r.height();
|
||||
|
||||
path.moveTo(bx, by + bh);
|
||||
path.lineTo(bx, by + radius);
|
||||
path.quadTo(bx, by, bx + radius, by);
|
||||
path.lineTo(bx + bw - radius, by);
|
||||
path.quadTo(bx + bw, by, bx + bw, by + radius);
|
||||
path.lineTo(bx + bw, by + bh);
|
||||
path.lineTo(bx, by + bh);
|
||||
path.closeSubpath();
|
||||
|
||||
p.drawPath(path);
|
||||
} else {
|
||||
p.drawRect(r);
|
||||
}
|
||||
|
||||
yCurrent -= segHeight;
|
||||
}
|
||||
|
||||
// draw label below bar
|
||||
QRect labelRect(left + i * (barWidth + spacing), bottom, barWidth, LABEL_H);
|
||||
QFont f = p.font();
|
||||
f.setBold(true);
|
||||
p.setFont(f);
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(labelRect, Qt::AlignCenter, bar.label);
|
||||
}
|
||||
}
|
||||
|
||||
void BarChartWidget::leaveEvent(QEvent *)
|
||||
{
|
||||
hoveredBar = -1;
|
||||
hoveredSegment = -1;
|
||||
QToolTip::hideText();
|
||||
}
|
||||
|
||||
void BarChartWidget::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
if (bars.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int PAD = 4;
|
||||
constexpr int LABEL_H = 20;
|
||||
int w = width();
|
||||
int h = height();
|
||||
int left = 46;
|
||||
int right = w - PAD;
|
||||
int top = PAD;
|
||||
int bottom = h - PAD - LABEL_H;
|
||||
int barAreaHeight = bottom - top;
|
||||
|
||||
int barCount = bars.size();
|
||||
int spacing = 6;
|
||||
int barWidth = (right - left - (barCount - 1) * spacing) / barCount;
|
||||
|
||||
// find hovered bar
|
||||
int mx = e->pos().x();
|
||||
hoveredBar = -1;
|
||||
for (int i = 0; i < barCount; i++) {
|
||||
int x0 = left + i * (barWidth + spacing);
|
||||
if (mx >= x0 && mx <= x0 + barWidth) {
|
||||
hoveredBar = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hoveredBar < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find hovered segment
|
||||
int yCurrent = bottom;
|
||||
const auto &segments = bars[hoveredBar].segments;
|
||||
hoveredSegment = -1;
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
const auto &seg = segments[i];
|
||||
int segHeight = (seg.value * barAreaHeight / highest);
|
||||
if (segHeight < 2 && seg.value > 0)
|
||||
segHeight = 2;
|
||||
|
||||
int topY = yCurrent - segHeight;
|
||||
int bottomY = yCurrent;
|
||||
if (e->pos().y() >= topY && e->pos().y() <= bottomY) {
|
||||
hoveredSegment = i;
|
||||
break;
|
||||
}
|
||||
yCurrent -= segHeight;
|
||||
}
|
||||
|
||||
if (hoveredSegment >= 0) {
|
||||
const auto &s = segments[hoveredSegment];
|
||||
QString text = QString("%1: %2 cards\n\n%3").arg(s.category).arg(s.value).arg(s.cards.join("\n"));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QToolTip::showText(e->globalPosition().toPoint(), text, this);
|
||||
#else
|
||||
QToolTip::showText(e->globalPos(), text, this);
|
||||
#endif
|
||||
} else {
|
||||
QToolTip::hideText();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef COCKATRICE_BAR_CHART_WIDGET_H
|
||||
#define COCKATRICE_BAR_CHART_WIDGET_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
|
||||
struct BarSegment
|
||||
{
|
||||
QString category;
|
||||
int value;
|
||||
QStringList cards;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
struct BarData
|
||||
{
|
||||
QString label;
|
||||
QVector<BarSegment> segments;
|
||||
};
|
||||
|
||||
class BarChartWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BarChartWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setBars(const QVector<BarData> &bars);
|
||||
void setHighest(int h); // global max for scaling
|
||||
int barCount() const
|
||||
{
|
||||
return bars.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
QVector<BarData> bars;
|
||||
int highest = 1; // global maximum value
|
||||
|
||||
int hoveredBar = -1;
|
||||
int hoveredSegment = -1;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_BAR_CHART_WIDGET_H
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "color_bar.h"
|
||||
|
||||
#include <QLinearGradient>
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#include "segmented_bar_widget.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QToolTip>
|
||||
|
||||
SegmentedBarWidget::SegmentedBarWidget(QString label, QVector<Segment> segments, int total, QWidget *parent)
|
||||
: QWidget(parent), label(std::move(label)), segments(std::move(segments)), total(total)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
setMinimumWidth(36);
|
||||
setMaximumWidth(50);
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
QSize SegmentedBarWidget::sizeHint() const
|
||||
{
|
||||
return QSize(50, 150);
|
||||
}
|
||||
|
||||
void SegmentedBarWidget::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
constexpr int PAD = 4;
|
||||
constexpr int LABEL_H = 20;
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
|
||||
int barX = PAD;
|
||||
int barWidth = w - PAD * 2;
|
||||
|
||||
int barTop = PAD;
|
||||
int barBottom = h - PAD - LABEL_H;
|
||||
int barHeight = barBottom - barTop;
|
||||
|
||||
int yCurrent = barBottom;
|
||||
|
||||
// draw stacked segments
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
const auto &seg = segments[i];
|
||||
|
||||
int segHeight = total > 0 ? (seg.value * barHeight / total) : 0;
|
||||
if (segHeight < 2)
|
||||
segHeight = 2;
|
||||
|
||||
QRect r(barX, yCurrent - segHeight, barWidth, segHeight);
|
||||
bool isTop = (i == segments.size() - 1);
|
||||
|
||||
QLinearGradient g(r.topLeft(), r.bottomLeft());
|
||||
g.setColorAt(0, seg.color.lighter(120));
|
||||
g.setColorAt(1, seg.color.darker(110));
|
||||
p.setBrush(g);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
if (isTop) {
|
||||
QPainterPath path;
|
||||
int radius = 6;
|
||||
|
||||
int x = r.x();
|
||||
int y = r.y();
|
||||
int w = r.width();
|
||||
int h = r.height();
|
||||
|
||||
path.moveTo(x, y + h);
|
||||
path.lineTo(x, y + radius);
|
||||
path.quadTo(x, y, x + radius, y);
|
||||
path.lineTo(x + w - radius, y);
|
||||
path.quadTo(x + w, y, x + w, y + radius);
|
||||
path.lineTo(x + w, y + h);
|
||||
path.lineTo(x, y + h);
|
||||
path.closeSubpath();
|
||||
|
||||
p.drawPath(path);
|
||||
} else {
|
||||
p.drawRect(r);
|
||||
}
|
||||
|
||||
yCurrent -= segHeight;
|
||||
}
|
||||
|
||||
// draw label
|
||||
QRect labelRect(0, h - LABEL_H, w, LABEL_H);
|
||||
QFont f = p.font();
|
||||
f.setBold(true);
|
||||
p.setFont(f);
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(labelRect, Qt::AlignCenter, label);
|
||||
}
|
||||
|
||||
int SegmentedBarWidget::segmentAt(int y) const
|
||||
{
|
||||
int padding = 4;
|
||||
int labelHeight = 20;
|
||||
int barHeight = height() - padding * 2 - labelHeight;
|
||||
int barTop = padding;
|
||||
int barBottom = barTop + barHeight;
|
||||
|
||||
int currentTop = barBottom;
|
||||
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
int segHeight = total > 0 ? (segments[i].value * barHeight / total) : 0;
|
||||
if (segHeight < 1) {
|
||||
segHeight = 1;
|
||||
}
|
||||
|
||||
int top = currentTop - segHeight;
|
||||
int bottom = currentTop;
|
||||
|
||||
if (y >= top && y <= bottom)
|
||||
return i;
|
||||
|
||||
currentTop -= segHeight;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SegmentedBarWidget::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
if (!hovered) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = segmentAt(e->pos().y());
|
||||
if (idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Segment &s = segments[idx];
|
||||
QString text = QString("%1: %2 cards\n%3").arg(s.category).arg(s.value).arg(s.cards.join(", "));
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QToolTip::showText(e->globalPosition().toPoint(), text, this);
|
||||
#else
|
||||
QToolTip::showText(e->globalPos(), text, this);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef COCKATRICE_SEGMENTED_BAR_WIDGET_H
|
||||
#define COCKATRICE_SEGMENTED_BAR_WIDGET_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
|
||||
class SegmentedBarWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Segment
|
||||
{
|
||||
QString category;
|
||||
int value = 0;
|
||||
QStringList cards;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
QString label;
|
||||
QVector<Segment> segments;
|
||||
float total = 1.0;
|
||||
|
||||
explicit SegmentedBarWidget(QString label, QVector<Segment> segments, int total, QWidget *parent = nullptr);
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
|
||||
int segmentAt(int y) const;
|
||||
|
||||
private:
|
||||
bool hovered = true;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_SEGMENTED_BAR_WIDGET_H
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
#include "color_pie.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QToolTip>
|
||||
#include <QtMath>
|
||||
|
||||
ColorPie::ColorPie(const QMap<QString, int> &_colors, QWidget *parent) : QWidget(parent), colors(_colors)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void ColorPie::setColors(const QMap<QString, int> &_colors)
|
||||
{
|
||||
colors = _colors;
|
||||
update();
|
||||
}
|
||||
|
||||
QSize ColorPie::minimumSizeHint() const
|
||||
{
|
||||
return QSize(200, 200);
|
||||
}
|
||||
|
||||
void ColorPie::paintEvent(QPaintEvent *)
|
||||
{
|
||||
if (colors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
for (int v : colors.values()) {
|
||||
total += v;
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
int size = qMin(w, h) - 40; // leave space for labels
|
||||
QRectF rect((w - size) / 2.0, (h - size) / 2.0, size, size);
|
||||
|
||||
// Draw border
|
||||
p.setPen(QPen(Qt::black, 1));
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawEllipse(rect);
|
||||
|
||||
// Sorted keys for predictable order
|
||||
QList<QString> sortedKeys = colors.keys();
|
||||
std::sort(sortedKeys.begin(), sortedKeys.end());
|
||||
|
||||
double startAngle = 0.0;
|
||||
|
||||
for (const QString &key : sortedKeys) {
|
||||
int value = colors[key];
|
||||
double ratio = double(value) / total;
|
||||
|
||||
if (ratio <= minRatioThreshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double spanAngle = ratio * 360.0;
|
||||
|
||||
QColor base = colorFromName(key);
|
||||
|
||||
// Gradient
|
||||
QRadialGradient grad(rect.center(), size / 2);
|
||||
grad.setColorAt(0, base.lighter(130));
|
||||
grad.setColorAt(1, base.darker(130));
|
||||
p.setBrush(grad);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
// Draw slice
|
||||
p.drawPie(rect, int(startAngle * 16), int(spanAngle * 16));
|
||||
|
||||
// Draw percent label
|
||||
double midAngle = startAngle + spanAngle / 2;
|
||||
double rad = qDegreesToRadians(midAngle);
|
||||
double labelRadius = size / 2 + 15; // slightly outside the pie
|
||||
QPointF center = rect.center();
|
||||
QPointF labelPos(center.x() + labelRadius * qCos(rad), center.y() - labelRadius * qSin(rad));
|
||||
|
||||
QString label = QString("%1%").arg(int(ratio * 100 + 0.5));
|
||||
|
||||
QFontMetrics fm(p.font());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
|
||||
int labelWidth = fm.horizontalAdvance(label);
|
||||
#else
|
||||
int labelWidth = fm.width(label);
|
||||
#endif
|
||||
QRectF textRect(labelPos.x() - labelWidth / 2.0, labelPos.y() - fm.height() / 2.0, labelWidth, fm.height());
|
||||
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(textRect, Qt::AlignCenter, label);
|
||||
|
||||
startAngle += spanAngle;
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
void ColorPie::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
isHovered = true;
|
||||
}
|
||||
#else
|
||||
void ColorPie::enterEvent(QEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
isHovered = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ColorPie::leaveEvent(QEvent *)
|
||||
{
|
||||
isHovered = false;
|
||||
}
|
||||
|
||||
void ColorPie::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!isHovered || colors.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QPoint p = event->position().toPoint();
|
||||
QPoint gp = event->globalPosition().toPoint();
|
||||
#else
|
||||
QPoint p = event->pos();
|
||||
QPoint gp = event->globalPos();
|
||||
#endif
|
||||
|
||||
QString text = tooltipForPoint(p);
|
||||
if (!text.isEmpty()) {
|
||||
QToolTip::showText(gp, text, this);
|
||||
}
|
||||
}
|
||||
|
||||
QString ColorPie::tooltipForPoint(const QPoint &pt) const
|
||||
{
|
||||
if (colors.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
for (int v : colors.values())
|
||||
total += v;
|
||||
if (total == 0)
|
||||
return {};
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
int size = qMin(w, h) - 40;
|
||||
QPointF center(w / 2.0, h / 2.0);
|
||||
|
||||
QPointF v = pt - center;
|
||||
double distance = std::sqrt(v.x() * v.x() + v.y() * v.y());
|
||||
if (distance > size / 2.0)
|
||||
return {}; // outside pie
|
||||
|
||||
double angle = std::atan2(-v.y(), v.x()) * 180.0 / M_PI;
|
||||
if (angle < 0) {
|
||||
angle += 360.0;
|
||||
}
|
||||
|
||||
double acc = 0.0;
|
||||
|
||||
QList<QString> keys = colors.keys();
|
||||
std::sort(keys.begin(), keys.end());
|
||||
|
||||
for (const QString &key : keys) {
|
||||
double span = (double(colors[key]) / total) * 360.0;
|
||||
|
||||
if (angle >= acc && angle < acc + span) {
|
||||
double percent = (100.0 * colors[key]) / total;
|
||||
return QString("%1: %2 cards (%3%)").arg(key).arg(colors[key]).arg(QString::number(percent, 'f', 1));
|
||||
}
|
||||
acc += span;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QColor ColorPie::colorFromName(const QString &name) const
|
||||
{
|
||||
static QMap<QString, QColor> map = {
|
||||
{"R", QColor(220, 30, 30)}, {"G", QColor(40, 170, 40)}, {"U", QColor(40, 90, 200)},
|
||||
{"W", QColor(235, 235, 230)}, {"B", QColor(30, 30, 30)},
|
||||
};
|
||||
|
||||
if (map.contains(name)) {
|
||||
return map[name];
|
||||
}
|
||||
|
||||
QColor c(name);
|
||||
if (!c.isValid()) {
|
||||
c = Qt::gray;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef COCKATRICE_COLOR_PIE_H
|
||||
#define COCKATRICE_COLOR_PIE_H
|
||||
|
||||
#ifndef COLOR_PIE_H
|
||||
#define COLOR_PIE_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class ColorPie : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ColorPie(const QMap<QString, int> &_colors = {}, QWidget *parent = nullptr);
|
||||
|
||||
void setColors(const QMap<QString, int> &_colors);
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
#else
|
||||
void enterEvent(QEvent *event) override;
|
||||
#endif
|
||||
void leaveEvent(QEvent *) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
QMap<QString, int> colors;
|
||||
bool isHovered = false;
|
||||
const double minRatioThreshold = 0.01; // skip tiny slices
|
||||
|
||||
QColor colorFromName(const QString &name) const;
|
||||
QString tooltipForPoint(const QPoint &pt) const;
|
||||
};
|
||||
|
||||
#endif // COLOR_PIE_H
|
||||
|
||||
#endif // COCKATRICE_COLOR_PIE_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue