From bce2a61c3cdb25adf09344cf08a8b68473f68c41 Mon Sep 17 00:00:00 2001
From: cawtds <38510667+cawtds@users.noreply.github.com>
Date: Sun, 19 Jul 2026 13:08:14 +0200
Subject: [PATCH] redesign species distribution
---
forms/wildmonchart.ui | 10 ++--
include/ui/specieschartview.h | 37 ++++++++++++
include/ui/wildmonchart.h | 5 +-
porymap.pro | 2 +
src/mainwindow.cpp | 2 +-
src/ui/specieschartview.cpp | 100 +++++++++++++++++++++++++++++++
src/ui/wildmonchart.cpp | 108 +++++++++++++++++++++-------------
7 files changed, 216 insertions(+), 48 deletions(-)
create mode 100644 include/ui/specieschartview.h
create mode 100644 src/ui/specieschartview.cpp
diff --git a/forms/wildmonchart.ui b/forms/wildmonchart.ui
index fec086dd..c4a4a8e5 100644
--- a/forms/wildmonchart.ui
+++ b/forms/wildmonchart.ui
@@ -92,7 +92,7 @@
0
-
-
+
QPainter::Antialiasing
@@ -216,7 +216,7 @@
-
-
+
QPainter::Antialiasing
@@ -230,9 +230,9 @@
- QChartView
- QGraphicsView
-
+ SpeciesChartView
+ QChartView
+
NoScrollComboBox
diff --git a/include/ui/specieschartview.h b/include/ui/specieschartview.h
new file mode 100644
index 00000000..cfb66be8
--- /dev/null
+++ b/include/ui/specieschartview.h
@@ -0,0 +1,37 @@
+#ifndef SPECIESCHARTVIEW_H
+#define SPECIESCHARTVIEW_H
+
+#include
+
+class Project;
+
+struct SpeciesEntry
+{
+ QString name;
+ QPixmap icon;
+};
+
+class SpeciesChartView : public QChartView
+{
+ Q_OBJECT
+
+public:
+ static constexpr int IconSize = 32;
+ static constexpr int IconVerticalOffset = -2;
+ static constexpr int Spacing = 6;
+ static constexpr int Padding = 10;
+ explicit SpeciesChartView(QWidget *parent = nullptr);
+
+ void setSpecies(const QStringList &species);
+ void setProject(Project *project);
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+
+private:
+ QVector m_speciesEntries;
+ Project *m_project = nullptr;
+ int m_maxTextWidth = 0;
+};
+
+#endif
diff --git a/include/ui/wildmonchart.h b/include/ui/wildmonchart.h
index 0ebc26f0..0b0d6f9a 100644
--- a/include/ui/wildmonchart.h
+++ b/include/ui/wildmonchart.h
@@ -8,6 +8,8 @@
#if __has_include()
#include
+class Project;
+
namespace Ui {
class WildMonChart;
}
@@ -16,7 +18,7 @@ class WildMonChart : public QWidget
{
Q_OBJECT
public:
- explicit WildMonChart(QWidget *parent, const EncounterTableModel *table);
+ explicit WildMonChart(QWidget *parent, const EncounterTableModel *table, Project *project);
~WildMonChart();
public slots:
@@ -27,6 +29,7 @@ public slots:
private:
Ui::WildMonChart *ui;
const EncounterTableModel *table;
+ Project *project = nullptr;
QStringList groupNames;
QStringList groupNamesReversed;
diff --git a/porymap.pro b/porymap.pro
index d2463f5d..199dc26b 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -144,6 +144,7 @@ SOURCES += src/config/keyvalueconfigbase.cpp \
src/ui/palettecolorsearch.cpp \
src/ui/paletteeditor.cpp \
src/ui/selectablepixmapitem.cpp \
+ src/ui/specieschartview.cpp \
src/ui/tileseteditor.cpp \
src/ui/tileseteditormetatileselector.cpp \
src/ui/tileseteditortileselector.cpp \
@@ -275,6 +276,7 @@ HEADERS += include/config/keyvalueconfigbase.h \
include/ui/palettecolorsearch.h \
include/ui/paletteeditor.h \
include/ui/selectablepixmapitem.h \
+ include/ui/specieschartview.h \
include/ui/tileseteditor.h \
include/ui/tileseteditormetatileselector.h \
include/ui/tileseteditortileselector.h \
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 6d19344a..5dd547c4 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2839,7 +2839,7 @@ void MainWindow::on_pushButton_DeleteWildMonGroup_clicked() {
void MainWindow::on_pushButton_SummaryChart_clicked() {
if (!this->wildMonChart) {
- this->wildMonChart = new WildMonChart(this, this->editor->getCurrentWildMonTable());
+ this->wildMonChart = new WildMonChart(this, this->editor->getCurrentWildMonTable(), this->editor->project);
connect(this->editor, &Editor::wildMonTableOpened, this->wildMonChart, &WildMonChart::setTable);
connect(this->editor, &Editor::wildMonTableClosed, this->wildMonChart, &WildMonChart::clearTable);
connect(this->editor, &Editor::wildMonTableEdited, this->wildMonChart, &WildMonChart::refresh);
diff --git a/src/ui/specieschartview.cpp b/src/ui/specieschartview.cpp
new file mode 100644
index 00000000..68a35eb6
--- /dev/null
+++ b/src/ui/specieschartview.cpp
@@ -0,0 +1,100 @@
+#include "specieschartview.h"
+
+#include "project.h"
+
+#include
+
+SpeciesChartView::SpeciesChartView(QWidget *parent)
+ : QChartView(parent)
+{
+}
+
+
+void SpeciesChartView::setSpecies(const QStringList &species)
+{
+ m_speciesEntries.clear();
+
+ const QString speciesPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix);
+
+ QFontMetrics fm(font());
+
+ m_maxTextWidth = 0;
+ for (const QString &name : species)
+ {
+ m_maxTextWidth = std::max(m_maxTextWidth, fm.horizontalAdvance(name));
+
+ QPixmap icon = m_project->getSpeciesIcon(speciesPrefix + name);
+
+ m_speciesEntries.push_back({
+ name,
+ icon.scaled(
+ IconSize,
+ IconSize,
+ Qt::KeepAspectRatio,
+ Qt::SmoothTransformation)
+ });
+ }
+
+ viewport()->update();
+}
+
+void SpeciesChartView::setProject(Project *project)
+{
+ m_project = project;
+ viewport()->update();
+}
+
+void SpeciesChartView::paintEvent(QPaintEvent *event)
+{
+ QChartView::paintEvent(event);
+
+ if (!chart() || !m_project || m_speciesEntries.isEmpty())
+ return;
+
+ QPainter painter(viewport());
+ painter.setRenderHint(QPainter::TextAntialiasing);
+ painter.setPen(chart()->titleBrush().color());
+
+ const QRectF plot = chart()->plotArea();
+
+ if (plot.height() <= 0)
+ return;
+
+ const QFontMetrics fm(painter.font());
+
+ const double rowHeight = plot.height() / m_speciesEntries.size();
+ const int labelWidth = m_maxTextWidth + IconSize + Spacing;
+ const int textX = plot.left() - labelWidth - Padding;
+ const int iconX = textX + m_maxTextWidth + Spacing;
+
+ for (int i = 0; i < m_speciesEntries.size(); ++i)
+ {
+ const SpeciesEntry &entry = m_speciesEntries[i];
+
+ // Center text vertically
+ const double y = plot.top() + rowHeight * (i + 0.5);
+ const QRect textRect(
+ textX,
+ static_cast(y - rowHeight / 2.0),
+ m_maxTextWidth,
+ static_cast(rowHeight)
+ );
+
+ painter.drawText(
+ textRect,
+ Qt::AlignRight | Qt::AlignVCenter,
+ entry.name
+ );
+
+ // Align icon with text
+ const int iconY = textRect.center().y() - IconSize / 2.0 + IconVerticalOffset;
+ const QRect iconRect(
+ iconX,
+ iconY,
+ IconSize,
+ IconSize
+ );
+
+ painter.drawPixmap(iconRect, entry.icon);
+ }
+}
diff --git a/src/ui/wildmonchart.cpp b/src/ui/wildmonchart.cpp
index 5419f6f4..ecdff29f 100644
--- a/src/ui/wildmonchart.cpp
+++ b/src/ui/wildmonchart.cpp
@@ -19,9 +19,10 @@ static const QList> themes = {
{"Qt", QChart::ChartThemeQt},
};
-WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) :
+WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table, Project *project) :
QWidget(parent),
- ui(new Ui::WildMonChart)
+ ui(new Ui::WildMonChart),
+ project(project)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
@@ -109,7 +110,7 @@ void WildMonChart::readTable() {
this->groupNames.append(QString());
this->groupNamesReversed.append(QString());
}
-
+
// Read data from the table, combining data for duplicate entries
const QVector tableFrequencies = this->table->percentages();
const QVector tablePokemon = this->table->encounterData().wildPokemon;
@@ -180,7 +181,11 @@ void WildMonChart::refresh() {
void WildMonChart::refreshSpeciesDistributionChart() {
if (ui->chartView_SpeciesDistribution->chart())
ui->chartView_SpeciesDistribution->chart()->deleteLater();
- ui->chartView_SpeciesDistribution->setChart(createSpeciesDistributionChart());
+
+ QChart *chart = createSpeciesDistributionChart();
+ ui->chartView_SpeciesDistribution->setChart(chart);
+ ui->chartView_SpeciesDistribution->setProject(this->project);
+ ui->chartView_SpeciesDistribution->setSpecies(this->speciesInLegendOrder);
if (ui->tabWidget->currentWidget() == ui->tabSpecies)
limitChartAnimation();
}
@@ -223,64 +228,85 @@ bool WildMonChart::usesGroupLabels() const {
return this->groupNames.length() > 1;
}
+struct SpeciesFrequency
+{
+ QString name;
+ double frequency;
+};
+
QChart* WildMonChart::createSpeciesDistributionChart() {
QList barSets;
+ QStringList categories;
+ QList speciesList;
+
for (const auto &species : getSpeciesNamesAlphabetical()) {
- // Add encounter chance data
- auto set = new QBarSet(species);
- for (auto groupName : this->groupNamesReversed)
- set->append(getSpeciesFrequency(species, groupName) * 100);
-
- // We order the bar sets from lowest to highest total, left-to-right.
- for (int i = 0; i < barSets.length() + 1; i++){
- if (i >= barSets.length() || barSets.at(i)->sum() > set->sum()) {
- barSets.insert(i, set);
- break;
- }
+ for (auto groupName : this->groupNamesReversed) {
+ speciesList.append({species, getSpeciesFrequency(species, groupName) * 100.0});
}
-
- // Show species name and % when hovering over a bar set. This covers some shortfalls in our ability to control the chart design
- // (i.e. bar segments may be too narrow to see the % label, or colors may be hard to match to the legend).
- connect(set, &QBarSet::hovered, [set] (bool on, int i) {
- QString text = on ? QString("%1 (%2%)").arg(set->label()).arg(set->at(i)) : "";
- QToolTip::showText(QCursor::pos(), text);
- });
}
- // Preserve the order we set earlier so that the legend isn't shuffling around for the other all-species charts.
- this->speciesInLegendOrder.clear();
- for (auto set : barSets)
- this->speciesInLegendOrder.append(set->label());
+ std::sort(speciesList.begin(), speciesList.end(), [](const auto &a, const auto &b) {
+ return a.frequency < b.frequency;
+ });
+
+ auto *set = new QBarSet("Encounter Rates");
+ for (const auto &entry : speciesList) {
+ set->append(entry.frequency);
+ categories.append(entry.name);
+ }
+
+ barSets.append(set);
+
+ // Show species name and % when hovering over a bar set. This covers some shortfalls in our ability to control the chart design
+ // (i.e. bar segments may be too narrow to see the % label).
+ connect(set, &QBarSet::hovered, [set] (bool on, int i) {
+ QString text = on ? QString("%1 (%2%)").arg(set->label()).arg(set->at(i)) : "";
+ QToolTip::showText(QCursor::pos(), text);
+ });
+
+ // Reverse the order for specieschartview
+ this->speciesInLegendOrder = categories;
+ std::reverse(speciesInLegendOrder.begin(),
+ speciesInLegendOrder.end());
// Set up series
- auto series = new QHorizontalPercentBarSeries();
+ auto series = new QHorizontalBarSeries();
series->setLabelsVisible();
series->append(barSets);
+ series->setBarWidth(0.8);
// Set up chart
auto chart = new QChart();
chart->addSeries(series);
chart->setTheme(currentTheme());
chart->setAnimationOptions(QChart::SeriesAnimations);
- chart->legend()->setVisible(true);
- chart->legend()->setShowToolTips(true);
- chart->legend()->setAlignment(Qt::AlignBottom);
- saveSpeciesColors(barSets);
+ chart->legend()->hide();
+
+ QFontMetrics fm(chart->font());
+
+ int maxTextWidth = 0;
+ for (const QString &category : categories) {
+ maxTextWidth = std::max(maxTextWidth, fm.horizontalAdvance(category));
+ }
+
+ int labelWidth = maxTextWidth + SpeciesChartView::IconSize + SpeciesChartView::Spacing;
+ int leftMargin = labelWidth + 2 * SpeciesChartView::Padding;
+ chart->setMargins(QMargins(leftMargin, 20, 20, 20));
// X-axis is the % frequency. We're already showing percentages on the bar, so we just display 0/50/100%
auto axisX = new QValueAxis();
+ axisX->setRange(0, 100);
axisX->setLabelFormat("%u%%");
- axisX->setTickCount(3);
+ axisX->setTickCount(6);
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);
- // Y-axis is the names of encounter groups (e.g. Old Rod, Good Rod...)
- if (usesGroupLabels()) {
- auto axisY = new QBarCategoryAxis();
- axisY->setCategories(this->groupNamesReversed);
- chart->addAxis(axisY, Qt::AlignLeft);
- series->attachAxis(axisY);
- }
+ // Y-axis is species names + icons
+ auto axisY = new QBarCategoryAxis();
+ axisY->setCategories(categories);
+ axisY->setLabelsVisible(false); // handled in specieschartview.c/h
+ chart->addAxis(axisY, Qt::AlignLeft);
+ series->attachAxis(axisY);
return chart;
}
@@ -351,7 +377,6 @@ QChart* WildMonChart::createLevelDistributionChart() {
chart->legend()->setVisible(true);
chart->legend()->setShowToolTips(true);
chart->legend()->setAlignment(Qt::AlignBottom);
- applySpeciesColors(barSets); // Has to happen after theme is set
// X-axis is the level range.
QBarCategoryAxis *axisX = new QBarCategoryAxis();
@@ -389,11 +414,12 @@ void WildMonChart::updateTheme() {
if (!chart || chart->series().isEmpty())
return;
chart->setTheme(theme);
- saveSpeciesColors(static_cast(chart->series().at(0))->barSets());
chart = ui->chartView_LevelDistribution->chart();
if (!chart || chart->series().isEmpty())
return;
+
+ saveSpeciesColors(static_cast(chart->series().at(0))->barSets());
chart->setTheme(theme);
applySpeciesColors(static_cast(chart->series().at(0))->barSets());
}