From 23d790cc4a196318e5c40d47da12d3459068c161 Mon Sep 17 00:00:00 2001 From: garak Date: Sat, 14 Jan 2023 23:10:58 -0500 Subject: [PATCH] use custom model for encounter tables --- include/core/wildmoninfo.h | 1 - include/ui/encountertabledelegates.h | 45 ++++++ include/ui/encountertablemodel.h | 50 +++++++ include/ui/montabwidget.h | 6 +- porymap.pro | 4 + src/core/wildmoninfo.cpp | 19 --- src/editor.cpp | 16 ++- src/mainwindow.cpp | 54 +++++++- src/ui/encountertabledelegates.cpp | 94 +++++++++++++ src/ui/encountertablemodel.cpp | 198 +++++++++++++++++++++++++++ src/ui/montabwidget.cpp | 196 +++++--------------------- 11 files changed, 490 insertions(+), 193 deletions(-) create mode 100644 include/ui/encountertabledelegates.h create mode 100644 include/ui/encountertablemodel.h create mode 100644 src/ui/encountertabledelegates.cpp create mode 100644 src/ui/encountertablemodel.cpp diff --git a/include/core/wildmoninfo.h b/include/core/wildmoninfo.h index a42522e7..f0421948 100644 --- a/include/core/wildmoninfo.h +++ b/include/core/wildmoninfo.h @@ -30,6 +30,5 @@ struct EncounterField { typedef QVector EncounterFields; WildMonInfo getDefaultMonInfo(EncounterField field); -WildMonInfo copyMonInfoFromTab(QTableWidget *table, EncounterField monField); #endif // GUARD_WILDMONINFO_H diff --git a/include/ui/encountertabledelegates.h b/include/ui/encountertabledelegates.h new file mode 100644 index 00000000..9b68badd --- /dev/null +++ b/include/ui/encountertabledelegates.h @@ -0,0 +1,45 @@ +#pragma once +#ifndef ENCOUNTERTABLEDELEGATES_H +#define ENCOUNTERTABLEDELEGATES_H + +#include +#include + + + +class Project; + +class SpeciesComboDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + SpeciesComboDelegate(Project *project, QObject *parent = nullptr); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + Project *project = nullptr; +}; + + + +class SpinBoxDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + SpinBoxDelegate(Project *project, QObject *parent = nullptr); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + Project *project = nullptr; +}; + +#endif // ENCOUNTERTABLEDELEGATES_H diff --git a/include/ui/encountertablemodel.h b/include/ui/encountertablemodel.h new file mode 100644 index 00000000..e3d34e9e --- /dev/null +++ b/include/ui/encountertablemodel.h @@ -0,0 +1,50 @@ +#pragma once +#ifndef ENCOUNTERTABLEMODEL_H +#define ENCOUNTERTABLEMODEL_H + +#include "wildmoninfo.h" + +#include + + + +class Editor; + +class EncounterTableModel : public QAbstractTableModel { + Q_OBJECT + +public: + EncounterTableModel(WildMonInfo monInfo, EncounterFields allFields, int fieldIndex, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +public: + enum ColumnType { + Slot, Group, Species, MinLevel, MaxLevel, EncounterChance, SlotRatio, EncounterRate, Count + }; + + WildMonInfo encounterData(); + void resize(int rows, int cols); + +private: + WildMonInfo monInfo; + EncounterFields encounterFields; + int fieldIndex; + + int numRows = 0; + int numCols = 0; + + QList slotRatios; + QList groupNames; + QList slotPercentages; + +signals: + void edited(); +}; + +#endif // ENCOUNTERTABLEMODEL_H diff --git a/include/ui/montabwidget.h b/include/ui/montabwidget.h index c5aaab9a..00b7f874 100644 --- a/include/ui/montabwidget.h +++ b/include/ui/montabwidget.h @@ -17,14 +17,12 @@ public: ~MonTabWidget(){}; void populate(); - void populateTab(int tabIndex, WildMonInfo monInfo, QString fieldName); + void populateTab(int tabIndex, WildMonInfo monInfo); void clear(); - void createSpeciesTableRow(QTableWidget *table, WildPokemon mon, int index, QString fieldName); - void clearTableAt(int index); - QTableWidget *tableAt(int tabIndex); + QTableView *tableAt(int tabIndex); public slots: void setTabActive(int index, bool active = true); diff --git a/porymap.pro b/porymap.pro index b0e321a2..8d2d0ba4 100644 --- a/porymap.pro +++ b/porymap.pro @@ -70,6 +70,8 @@ SOURCES += src/core/block.cpp \ src/ui/noscrollcombobox.cpp \ src/ui/noscrollspinbox.cpp \ src/ui/montabwidget.cpp \ + src/ui/encountertablemodel.cpp \ + src/ui/encountertabledelegates.cpp \ src/ui/paletteeditor.cpp \ src/ui/selectablepixmapitem.cpp \ src/ui/tileseteditor.cpp \ @@ -154,6 +156,8 @@ HEADERS += include/core/block.h \ include/ui/noscrollcombobox.h \ include/ui/noscrollspinbox.h \ include/ui/montabwidget.h \ + include/ui/encountertablemodel.h \ + include/ui/encountertabledelegates.h \ include/ui/adjustingstackedwidget.h \ include/ui/paletteeditor.h \ include/ui/selectablepixmapitem.h \ diff --git a/src/core/wildmoninfo.cpp b/src/core/wildmoninfo.cpp index 2fe9be7a..91422e5b 100644 --- a/src/core/wildmoninfo.cpp +++ b/src/core/wildmoninfo.cpp @@ -14,22 +14,3 @@ WildMonInfo getDefaultMonInfo(EncounterField field) { return newInfo; } - -WildMonInfo copyMonInfoFromTab(QTableWidget *monTable, EncounterField monField) { - WildMonInfo newInfo; - QVector newWildMons; - - bool extraColumn = !monField.groups.empty(); - for (int row = 0; row < monTable->rowCount(); row++) { - WildPokemon newWildMon; - newWildMon.species = monTable->cellWidget(row, extraColumn ? 2 : 1)->findChild()->currentText(); - newWildMon.minLevel = monTable->cellWidget(row, extraColumn ? 3 : 2)->findChild()->value(); - newWildMon.maxLevel = monTable->cellWidget(row, extraColumn ? 4 : 3)->findChild()->value(); - newWildMons.append(newWildMon); - } - newInfo.active = true; - newInfo.wildPokemon = newWildMons; - newInfo.encounterRate = monTable->findChild()->value(); - - return newInfo; -} diff --git a/src/editor.cpp b/src/editor.cpp index 0db04d9e..46c775f0 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -7,6 +7,7 @@ #include "mapsceneeventfilter.h" #include "metatile.h" #include "montabwidget.h" +#include "encountertablemodel.h" #include "editcommands.h" #include "config.h" #include "scripting.h" @@ -233,7 +234,7 @@ void Editor::displayWildMonTables() { tabWidget->clearTableAt(tabIndex); if (project->wildMonData.contains(map->constantName) && header.wildMons[fieldName].active) { - tabWidget->populateTab(tabIndex, header.wildMons[fieldName], fieldName); + tabWidget->populateTab(tabIndex, header.wildMons[fieldName]); } else { tabWidget->setTabActive(tabIndex, false); } @@ -338,7 +339,9 @@ void Editor::addNewWildMonGroup(QWidget *window) { if (copyCheckbox->isChecked()) { MonTabWidget *copyFrom = static_cast(stack->widget(stackIndex)); if (copyFrom->isTabEnabled(tabIndex)) { - header.wildMons[fieldName] = copyMonInfoFromTab(copyFrom->tableAt(tabIndex), monField); + QTableView *monTable = copyFrom->tableAt(tabIndex); + EncounterTableModel *model = static_cast(monTable->model()); + header.wildMons[fieldName] = model->encounterData(); } else { header.wildMons[fieldName] = getDefaultMonInfo(monField); @@ -346,7 +349,7 @@ void Editor::addNewWildMonGroup(QWidget *window) { } else { header.wildMons[fieldName] = getDefaultMonInfo(monField); } - tabWidget->populateTab(tabIndex, header.wildMons[fieldName], fieldName); + tabWidget->populateTab(tabIndex, header.wildMons[fieldName]); } else { tabWidget->setTabActive(tabIndex, false); } @@ -656,14 +659,13 @@ void Editor::saveEncounterTabData() { if (!tabWidget->isTabEnabled(fieldIndex++)) continue; - QTableWidget *monTable = static_cast(tabWidget->widget(fieldIndex - 1)); - QVector newWildMons; - encounterHeader.wildMons[fieldName] = copyMonInfoFromTab(monTable, monField); + QTableView *monTable = tabWidget->tableAt(fieldIndex - 1); + EncounterTableModel *model = static_cast(monTable->model()); + encounterHeader.wildMons[fieldName] = model->encounterData(); } } } -// Update encounters for every map based on the new encounter JSON field data. void Editor::updateEncounterFields(EncounterFields newFields) { EncounterFields oldFields = project->wildMonFields; // Go through fields and determine whether we need to update a field. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 16d8a114..9034473f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1940,6 +1940,7 @@ void MainWindow::updateSelectedObjects() { selectedObject = current->getPixmapItem(); + QSignalBlocker b(this->ui->spinner_ObjectID); this->ui->spinner_ObjectID->setMinimum(event_offs); this->ui->spinner_ObjectID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_ObjectID->setValue(current->getEventIndex() + event_offs); @@ -1952,6 +1953,7 @@ void MainWindow::updateSelectedObjects() { selectedWarp = current->getPixmapItem(); + QSignalBlocker b(this->ui->spinner_WarpID); this->ui->spinner_WarpID->setMinimum(event_offs); this->ui->spinner_WarpID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_WarpID->setValue(current->getEventIndex() + event_offs); @@ -1964,6 +1966,7 @@ void MainWindow::updateSelectedObjects() { selectedTrigger = current->getPixmapItem(); + QSignalBlocker b(this->ui->spinner_TriggerID); this->ui->spinner_TriggerID->setMinimum(event_offs); this->ui->spinner_TriggerID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_TriggerID->setValue(current->getEventIndex() + event_offs); @@ -1976,6 +1979,7 @@ void MainWindow::updateSelectedObjects() { selectedBG = current->getPixmapItem(); + QSignalBlocker b(this->ui->spinner_BgID); this->ui->spinner_BgID->setMinimum(event_offs); this->ui->spinner_BgID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_BgID->setValue(current->getEventIndex() + event_offs); @@ -1988,6 +1992,7 @@ void MainWindow::updateSelectedObjects() { selectedHealspot = current->getPixmapItem(); + QSignalBlocker b(this->ui->spinner_HealID); this->ui->spinner_HealID->setMinimum(event_offs); this->ui->spinner_HealID->setMaximum(current->getMap()->events.value(eventGroup).length() + event_offs - 1); this->ui->spinner_HealID->setValue(current->getEventIndex() + event_offs); @@ -2870,7 +2875,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { } #include -void MainWindow::runSpeedTest() { +/*void MainWindow::runSpeedTest() { // 10 iterations of this function: // 1 - load project // 2 - switch map @@ -2929,4 +2934,51 @@ void MainWindow::runSpeedTest() { summary += "MIN: " + QString::number(min).leftJustified(6, ' ') + "MAX: " + QString::number(max).leftJustified(6, ' ') + "AVG: " + QString::number(avg) + "\n"; qDebug() << qUtf8Printable(summary); +}*/ + +#include +#include +//#include +void MainWindow::runSpeedTest() { + // + QList times; + + // groupedMapNames + QElapsedTimer timer; + + //qDebug() << editor->project->groupedMapNames.first(); + + QStringList group0 = editor->project->groupedMapNames.first(); + + QString firstMap = group0.first(); + setMap(firstMap); + + //std::srand(unsigned(std::time(0))); + std::shuffle(group0.begin(), group0.end(), std::default_random_engine(69420)); + + timer.start(); + for (QString mapName : group0) { + setMap(mapName); + times.append(timer.restart()); + } + + setMap(firstMap); + + qint64 min = std::numeric_limits::max(); + qint64 max = 0; + qint64 average = 0; + qint64 count = 0; + + for (qint64 val : times) { + if (val > max) max = val; + if (val < min) min = val; + average += val; + count++; + qDebug() << val; + } + + average /= count; + + qDebug() << "*** TIMING SUMMARY ***"; + qDebug().nospace() << "min: " << min << " " << "max: " << max << " " << "avg: " << average; } diff --git a/src/ui/encountertabledelegates.cpp b/src/ui/encountertabledelegates.cpp new file mode 100644 index 00000000..a0baec3e --- /dev/null +++ b/src/ui/encountertabledelegates.cpp @@ -0,0 +1,94 @@ +#include "encountertabledelegates.h" +#include "encountertablemodel.h" + +#include +#include "project.h" +#include "noscrollcombobox.h" + + + +SpeciesComboDelegate::SpeciesComboDelegate(Project *project, QObject *parent) : QStyledItemDelegate(parent) { + this->project = project; +} + +void SpeciesComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + QString species = index.data(Qt::DisplayRole).toString(); + + QPixmap pm; + if (!QPixmapCache::find(species, &pm)) { + pm.load(this->project->speciesToIconPath.value(species)); + QPixmapCache::insert(species, pm); + } + QPixmap monIcon = pm.copy(0, 0, 32, 32); + + painter->drawText(QRect(option.rect.topLeft() + QPoint(36, 0), option.rect.bottomRight()), Qt::AlignLeft | Qt::AlignVCenter, species); + painter->drawPixmap(QRect(option.rect.topLeft(), QSize(32, 32)), monIcon, monIcon.rect()); +} + +QWidget *SpeciesComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { + NoScrollComboBox *editor = new NoScrollComboBox(parent); + editor->setFrame(false); + editor->addItems(this->project->speciesToIconPath.keys()); + return editor; +} + +void SpeciesComboDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + QString species = index.data(Qt::EditRole).toString(); + NoScrollComboBox *combo = static_cast(editor); + combo->setCurrentText(species); +} + +void SpeciesComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + NoScrollComboBox *combo = static_cast(editor); + QString species = combo->currentText(); + model->setData(index, species, Qt::EditRole); +} + +void SpeciesComboDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const { + editor->setGeometry(option.rect); +} + + + +SpinBoxDelegate::SpinBoxDelegate(Project *project, QObject *parent) : QStyledItemDelegate(parent) { + this->project = project; +} + +QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const { + QSpinBox *editor = new QSpinBox(parent); + editor->setFrame(false); + + int col = index.column(); + if (col == EncounterTableModel::ColumnType::MinLevel) { + editor->setMinimum(this->project->miscConstants.value("min_level_define").toInt()); + editor->setMaximum(index.siblingAtColumn(EncounterTableModel::ColumnType::MaxLevel).data(Qt::EditRole).toInt()); + } + else if (col == EncounterTableModel::ColumnType::MaxLevel) { + editor->setMinimum(index.siblingAtColumn(EncounterTableModel::ColumnType::MinLevel).data(Qt::EditRole).toInt()); + editor->setMaximum(this->project->miscConstants.value("max_level_define").toInt()); + } + else if (col == EncounterTableModel::ColumnType::EncounterRate) { + editor->setMinimum(0); + editor->setMaximum(180); + } + + return editor; +} + +void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + int value = index.model()->data(index, Qt::EditRole).toInt(); + + QSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value); +} + +void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + QSpinBox *spinBox = static_cast(editor); + spinBox->interpretText(); + int value = spinBox->value(); + model->setData(index, value, Qt::EditRole); +} + +void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const { + editor->setGeometry(option.rect); +} diff --git a/src/ui/encountertablemodel.cpp b/src/ui/encountertablemodel.cpp new file mode 100644 index 00000000..51658ff7 --- /dev/null +++ b/src/ui/encountertablemodel.cpp @@ -0,0 +1,198 @@ +#include "encountertablemodel.h" +#include "editor.h" + + + +EncounterTableModel::EncounterTableModel(WildMonInfo info, EncounterFields fields, int index, QObject *parent) : QAbstractTableModel(parent) { + this->fieldIndex = index; + this->encounterFields = fields; + this->monInfo = info; + + this->resize(this->monInfo.wildPokemon.size(), ColumnType::Count); + + this->slotRatios = fields[fieldIndex].encounterRates; + + for (int r = 0; r < this->numRows; r++) { + this->groupNames.append(QString()); + this->slotPercentages.append(0.0); + } + + if (!this->encounterFields[this->fieldIndex].groups.empty()) { + for (auto groupKeyPair : fields[fieldIndex].groups) { + int groupTotal = 0; + for (int i : groupKeyPair.second) { + this->groupNames[i] = groupKeyPair.first; + groupTotal += this->slotRatios[i]; + } + for (int i : groupKeyPair.second) { + this->slotPercentages[i] = static_cast(this->slotRatios[i]) / static_cast(groupTotal); + } + } + } else { + int groupTotal = 0; + for (int chance : this->encounterFields[this->fieldIndex].encounterRates) { + groupTotal += chance; + } + for (int i = 0; i < this->slotPercentages.count(); i++) { + this->slotPercentages[i] = static_cast(this->slotRatios[i]) / static_cast(groupTotal); + } + } +} + +void EncounterTableModel::resize(int rows, int cols) { + this->numRows = rows; + this->numCols = cols; +} + +int EncounterTableModel::rowCount(const QModelIndex &) const { + return this->numRows; +} + +int EncounterTableModel::columnCount(const QModelIndex &) const { + return this->numCols; +} + +QVariant EncounterTableModel::data(const QModelIndex &index, int role) const { + int row = index.row(); + int col = index.column(); + + if (role == Qt::DisplayRole) { + switch (col) { + case ColumnType::Slot: + return row; + + case ColumnType::Group: + return this->groupNames[row]; + + case ColumnType::Species: + return this->monInfo.wildPokemon[row].species; + + case ColumnType::MinLevel: + return this->monInfo.wildPokemon[row].minLevel; + + case ColumnType::MaxLevel: + return this->monInfo.wildPokemon[row].maxLevel; + + case ColumnType::EncounterChance: + return QString::number(this->slotPercentages[row] * 100.0, 'f', 2) + "%"; + + case ColumnType::SlotRatio: + return this->slotRatios[row]; + + case ColumnType::EncounterRate: + if (row == 0) { + return this->monInfo.encounterRate; + } else { + return QVariant(); + } + + default: + return QString(); + } + } + else if (role == Qt::EditRole) { + switch (col) { + case ColumnType::Species: + return this->monInfo.wildPokemon[row].species; + + case ColumnType::MinLevel: + return this->monInfo.wildPokemon[row].minLevel; + + case ColumnType::MaxLevel: + return this->monInfo.wildPokemon[row].maxLevel; + + case ColumnType::EncounterRate: + if (row == 0) { + return this->monInfo.encounterRate; + } else { + return QVariant(); + } + + default: + return QVariant(); + } + } + + return QVariant(); +} + +QVariant EncounterTableModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case ColumnType::Slot: + return QString("Slot"); + case ColumnType::Group: + return QString("Group"); + case ColumnType::Species: + return QString("Species"); + case ColumnType::MinLevel: + return QString("Min Level"); + case ColumnType::MaxLevel: + return QString("Max Level"); + case ColumnType::EncounterChance: + return QString("Encounter Chance"); + case ColumnType::SlotRatio: + return QString("Slot Ratio"); + case ColumnType::EncounterRate: + return QString("Encounter Rate"); + default: + return QVariant(); + } + } + return QVariant(); +} + +bool EncounterTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if (role == Qt::EditRole) { + if (!checkIndex(index)) + return false; + + int row = index.row(); + int col = index.column(); + + switch (col) { + case ColumnType::Species: + this->monInfo.wildPokemon[row].species = value.toString(); + break; + + case ColumnType::MinLevel: + this->monInfo.wildPokemon[row].minLevel = value.toInt(); + break; + + case ColumnType::MaxLevel: + this->monInfo.wildPokemon[row].maxLevel = value.toInt(); + break; + + case ColumnType::EncounterRate: + this->monInfo.encounterRate = value.toInt(); + break; + + default: + return false; + } + emit edited(); + return true; + } + return false; +} + +Qt::ItemFlags EncounterTableModel::flags(const QModelIndex &index) const { + Qt::ItemFlags flags = Qt::NoItemFlags; + switch (index.column()) { + case ColumnType::Species: + case ColumnType::MinLevel: + case ColumnType::MaxLevel: + flags |= Qt::ItemIsEditable; + break; + case ColumnType::EncounterRate: + if (index.row() == 0) flags |= Qt::ItemIsEditable; + break; + default: + break; + } + return flags | QAbstractTableModel::flags(index); +} + +WildMonInfo EncounterTableModel::encounterData() { + return this->monInfo; +} diff --git a/src/ui/montabwidget.cpp b/src/ui/montabwidget.cpp index 765749ca..37897d5f 100644 --- a/src/ui/montabwidget.cpp +++ b/src/ui/montabwidget.cpp @@ -1,6 +1,10 @@ #include "montabwidget.h" #include "noscrollcombobox.h" #include "editor.h" +#include "encountertablemodel.h" +#include "encountertabledelegates.h" + + MonTabWidget::MonTabWidget(Editor *editor, QWidget *parent) : QTabWidget(parent) { this->editor = editor; @@ -26,11 +30,7 @@ void MonTabWidget::populate() { activeTabs = QVector(fields.size(), false); for (EncounterField field : fields) { - QTableWidget *table = new QTableWidget(this); - table->setEditTriggers(QAbstractItemView::NoEditTriggers); - table->setFocusPolicy(Qt::NoFocus); - table->setSelectionMode(QAbstractItemView::NoSelection); - table->setTabKeyNavigation(false); + QTableView *table = new QTableView(this); table->clearFocus(); addTab(table, field.name); } @@ -45,7 +45,7 @@ void MonTabWidget::askActivateTab(int tabIndex, QPoint menuPos) { QAction actionActivateTab(QString("Add %1 data for this map...").arg(tabText), this); connect(&actionActivateTab, &QAction::triggered, [=](){ clearTableAt(tabIndex); - populateTab(tabIndex, getDefaultMonInfo(editor->project->wildMonFields.at(tabIndex)), tabText); + populateTab(tabIndex, getDefaultMonInfo(editor->project->wildMonFields.at(tabIndex))); editor->saveEncounterTabData(); setCurrentIndex(tabIndex); emit editor->wildMonDataChanged(); @@ -55,173 +55,47 @@ void MonTabWidget::askActivateTab(int tabIndex, QPoint menuPos) { } void MonTabWidget::clearTableAt(int tabIndex) { - QTableWidget *table = tableAt(tabIndex); + QTableView *table = tableAt(tabIndex); if (table) { - table->clear(); + table->setModel(nullptr); table->horizontalHeader()->hide(); } } -void MonTabWidget::populateTab(int tabIndex, WildMonInfo monInfo, QString fieldName) { - QTableWidget *speciesTable = tableAt(tabIndex); +void MonTabWidget::populateTab(int tabIndex, WildMonInfo monInfo) { + QTableView *speciesTable = tableAt(tabIndex); - int fieldIndex = 0; - for (EncounterField field : editor->project->wildMonFields) { - if (field.name == fieldName) break; - fieldIndex++; + EncounterTableModel *model = new EncounterTableModel(monInfo, editor->project->wildMonFields, tabIndex, this); + connect(model, &EncounterTableModel::edited, editor, &Editor::saveEncounterTabData); + speciesTable->setModel(model); + + speciesTable->setItemDelegateForColumn(EncounterTableModel::ColumnType::Species, new SpeciesComboDelegate(editor->project, this)); + speciesTable->setItemDelegateForColumn(EncounterTableModel::ColumnType::MinLevel, new SpinBoxDelegate(editor->project, this)); + speciesTable->setItemDelegateForColumn(EncounterTableModel::ColumnType::MaxLevel, new SpinBoxDelegate(editor->project, this)); + speciesTable->setItemDelegateForColumn(EncounterTableModel::ColumnType::EncounterRate, new SpinBoxDelegate(editor->project, this)); + + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::Slot, QHeaderView::ResizeToContents ); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::Group, QHeaderView::ResizeToContents ); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::Species, QHeaderView::Stretch); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::MinLevel, QHeaderView::Stretch); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::MaxLevel, QHeaderView::Stretch); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::SlotRatio, QHeaderView::Stretch); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::EncounterChance, QHeaderView::ResizeToContents); + speciesTable->horizontalHeader()->setSectionResizeMode(EncounterTableModel::ColumnType::EncounterRate, QHeaderView::ResizeToContents); + + // give enough vertical space for icons + margins + speciesTable->verticalHeader()->setMinimumSectionSize(40); + + if (editor->project->wildMonFields[tabIndex].groups.empty()) { + speciesTable->setColumnHidden(1, true); } - bool insertGroupLabel = false; - if (!editor->project->wildMonFields[fieldIndex].groups.empty()) insertGroupLabel = true; - speciesTable->setRowCount(monInfo.wildPokemon.size()); - speciesTable->setColumnCount(insertGroupLabel ? 8 : 7); - - QStringList landMonTableHeaders; - landMonTableHeaders << "Slot"; - if (insertGroupLabel) landMonTableHeaders << "Group"; - landMonTableHeaders << "Species" << "Min Level" << "Max Level" - << "Encounter Chance" << "Slot Ratio" << "Encounter Rate"; - speciesTable->setHorizontalHeaderLabels(landMonTableHeaders); speciesTable->horizontalHeader()->show(); - speciesTable->verticalHeader()->hide(); - speciesTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - speciesTable->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - - speciesTable->setShowGrid(false); - - QFrame *encounterFrame = new QFrame; - QHBoxLayout *encounterLayout = new QHBoxLayout; - - QSpinBox *encounterRate = new QSpinBox; - encounterRate->setMinimum(0); - encounterRate->setMaximum(180); - encounterRate->setValue(monInfo.encounterRate); - connect(encounterRate, QOverload::of(&QSpinBox::valueChanged), [this](int) { - editor->saveEncounterTabData(); - emit editor->wildMonDataChanged(); - }); - encounterLayout->addWidget(encounterRate); - encounterFrame->setLayout(encounterLayout); - speciesTable->setCellWidget(0, insertGroupLabel? 7 : 6, encounterFrame); - - int i = 0; - for (WildPokemon mon : monInfo.wildPokemon) { - createSpeciesTableRow(speciesTable, mon, i++, fieldName); - } this->setTabActive(tabIndex, true); } -void MonTabWidget::createSpeciesTableRow(QTableWidget *table, WildPokemon mon, int index, QString fieldName) { - QPixmap monIcon = QPixmap(editor->project->speciesToIconPath.value(mon.species)).copy(0, 0, 32, 32); - - QLabel *monNum = new QLabel(QString("%1.").arg(QString::number(index))); - - QLabel *monLabel = new QLabel(); - monLabel->setPixmap(monIcon); - - NoScrollComboBox *monSelector = new NoScrollComboBox; - monSelector->addItems(editor->project->speciesToIconPath.keys()); - monSelector->setCurrentText(mon.species); - monSelector->setEditable(true); - - QObject::connect(monSelector, &QComboBox::currentTextChanged, [=](QString newSpecies) { - QPixmap monIcon = QPixmap(editor->project->speciesToIconPath.value(newSpecies)).copy(0, 0, 32, 32); - monLabel->setPixmap(monIcon); - emit editor->wildMonDataChanged(); - if (!monIcon.isNull()) editor->saveEncounterTabData(); - }); - - QSpinBox *minLevel = new QSpinBox; - QSpinBox *maxLevel = new QSpinBox; - minLevel->setMinimum(editor->project->miscConstants.value("min_level_define").toInt()); - minLevel->setMaximum(editor->project->miscConstants.value("max_level_define").toInt()); - maxLevel->setMinimum(editor->project->miscConstants.value("min_level_define").toInt()); - maxLevel->setMaximum(editor->project->miscConstants.value("max_level_define").toInt()); - minLevel->setValue(mon.minLevel); - maxLevel->setValue(mon.maxLevel); - - // Connect level spinboxes so max is never less than min. - connect(minLevel, QOverload::of(&QSpinBox::valueChanged), [maxLevel, this](int min) { - maxLevel->setMinimum(min); - editor->saveEncounterTabData(); - emit editor->wildMonDataChanged(); - }); - - connect(maxLevel, QOverload::of(&QSpinBox::valueChanged), [this](int) { - editor->saveEncounterTabData(); - emit editor->wildMonDataChanged(); - }); - - int fieldIndex = 0; - for (EncounterField field : editor->project->wildMonFields) { - if (field.name == fieldName) break; - fieldIndex++; - } - - double slotChanceTotal = 0.0; - if (!editor->project->wildMonFields[fieldIndex].groups.empty()) { - for (auto groupKeyPair : editor->project->wildMonFields[fieldIndex].groups) { - QString groupKey = groupKeyPair.first; - if (editor->project->wildMonFields[fieldIndex].groups[groupKey].contains(index)) { - for (int chanceIndex : editor->project->wildMonFields[fieldIndex].groups[groupKey]) { - slotChanceTotal += static_cast(editor->project->wildMonFields[fieldIndex].encounterRates[chanceIndex]); - } - break; - } - } - } else { - for (auto chance : editor->project->wildMonFields[fieldIndex].encounterRates) { - slotChanceTotal += static_cast(chance); - } - } - - QLabel *percentLabel = new QLabel(QString("%1%").arg( - QString::number(editor->project->wildMonFields[fieldIndex].encounterRates[index] / slotChanceTotal * 100.0, 'f', 2) - )); - QLabel *ratioLabel = new QLabel(QString("%1").arg( - QString::number(editor->project->wildMonFields[fieldIndex].encounterRates[index] - ))); - - QFrame *speciesSelector = new QFrame; - QHBoxLayout *speciesSelectorLayout = new QHBoxLayout; - speciesSelectorLayout->addWidget(monLabel); - speciesSelectorLayout->addWidget(monSelector); - speciesSelector->setLayout(speciesSelectorLayout); - - // Prevent the spinboxes from being stupidly tall. - QFrame *minLevelFrame = new QFrame; - QVBoxLayout *minLevelSpinboxLayout = new QVBoxLayout; - minLevelSpinboxLayout->addWidget(minLevel); - minLevelFrame->setLayout(minLevelSpinboxLayout); - QFrame *maxLevelFrame = new QFrame; - QVBoxLayout *maxLevelSpinboxLayout = new QVBoxLayout; - maxLevelSpinboxLayout->addWidget(maxLevel); - maxLevelFrame->setLayout(maxLevelSpinboxLayout); - - bool insertGroupLabel = false; - if (!editor->project->wildMonFields[fieldIndex].groups.empty()) insertGroupLabel = true; - table->setCellWidget(index, 0, monNum); - if (insertGroupLabel) { - QString groupName = QString(); - for (auto groupKeyPair : editor->project->wildMonFields[fieldIndex].groups) { - QString groupKey = groupKeyPair.first; - if (editor->project->wildMonFields[fieldIndex].groups[groupKey].contains(index)) { - groupName = groupKey; - break; - } - } - QLabel *groupNameLabel = new QLabel(groupName); - table->setCellWidget(index, 1, groupNameLabel); - } - table->setCellWidget(index, insertGroupLabel? 2 : 1, speciesSelector); - table->setCellWidget(index, insertGroupLabel? 3 : 2, minLevelFrame); - table->setCellWidget(index, insertGroupLabel? 4 : 3, maxLevelFrame); - table->setCellWidget(index, insertGroupLabel? 5 : 4, percentLabel); - table->setCellWidget(index, insertGroupLabel? 6 : 5, ratioLabel); -} - -QTableWidget *MonTabWidget::tableAt(int tabIndex) { - return static_cast(this->widget(tabIndex)); +QTableView *MonTabWidget::tableAt(int tabIndex) { + return static_cast(this->widget(tabIndex)); } void MonTabWidget::setTabActive(int index, bool active) {