view include/gui/widgets/graph.h @ 365:f81bed4e04ac

*: megacommit that probably breaks things
author Paper <paper@paper.us.eu.org>
date Wed, 02 Oct 2024 23:06:43 -0400
parents f31305b9f60a
children
line wrap: on
line source

#ifndef MINORI_GUI_WIDGETS_GRAPH_H_
#define MINORI_GUI_WIDGETS_GRAPH_H_

/* This class is defined as a template, so that means everything gets defined here as well :) */

#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPen>
#include <QRect>
#include <QSize>
#include <QWidget>
#include <algorithm>
#include <unordered_map>

template<typename T>
class Graph final : public QWidget {
public:
	Graph(QWidget* parent = nullptr) : QWidget(parent){};
	void AddItem(T key, unsigned long val) {
		map[key] = val;
		update();
		updateGeometry();
	};
	void Clear() {
		map.clear();
		update();
		updateGeometry();
	};

protected:
	static constexpr int HORIZ_SPACING = 5;
	static constexpr int VERT_SPACING = 3;

	std::unordered_map<T, unsigned long> map = {};

	QSize minimumSizeHint() const override {
		QFontMetrics metric(font());
		/* wtf?... */
		return QSize(100, (metric.height() * map.size()) + (VERT_SPACING * map.size()));
	}

	/* helper functions */
	inline unsigned long GetTotal() {
		unsigned long count = 0;

		for (const auto& item : map)
			count += item.second;

		return count;
	}

	inline unsigned long GetTextWidth() {
		unsigned long ret = 0;
		QFontMetrics metric(font());

		for (const auto& item : map) {
			unsigned long width = metric.boundingRect(QString::number(item.first)).width();
			if (width > ret)
				ret = width;
		}

		return ret;
	}

	inline unsigned long GetValueWidth() {
		unsigned long ret = 0;
		QFontMetrics metric(font());

		for (const auto& item : map) {
			unsigned long width = metric.boundingRect(QString::number(item.second)).width();
			if (width > ret)
				ret = width;
		}

		return ret;
	}

	void paintEvent(QPaintEvent* event) override {
		/* these are retrieved from the QPaintEvent */
		const QRect rect = event->rect();
		const int width = rect.width();
		const int x = rect.x();
		int y = rect.y();

		/* these are calculated from font metrics and such */
		const int total = GetTotal();
		const int text_width = GetTextWidth();
		const int value_width = GetValueWidth();
		const int each_height = QFontMetrics(font()).height();

		/* now we do the actual painting */
		QPainter painter(this);

		for (const auto& [key, value] : map) {
			int offset = 0;

			painter.drawText(QRect(x, y, text_width, each_height), Qt::AlignVCenter | Qt::AlignRight,
			                 QString::number(key));

			offset += text_width + HORIZ_SPACING;

			/* only draw this if we actually have any data */
			if (total) {
				const int rect_width = (static_cast<double>(value) / total) * (width - offset - HORIZ_SPACING - value_width);

				painter.save();

				QPen pen(painter.pen());
				pen.setStyle(Qt::NoPen);
				painter.setPen(pen);

				QPainterPath path;
				path.addRect(x + offset, y, rect_width, each_height);
				painter.fillPath(path, Qt::darkGreen);
				painter.drawPath(path);

				offset += rect_width;

				painter.restore();
			}

			offset += HORIZ_SPACING;

			painter.drawText(QRect(x + offset, y, value_width, each_height), Qt::AlignVCenter | Qt::AlignLeft, QString::number(value));

			y += each_height + VERT_SPACING;
		}
	}
};

#endif // MINORI_GUI_WIDGETS_GRAPH_H_