mirror of https://github.com/mgba-emu/mgba.git
Qt: Revamp BattleChipView, add drag and drop
This commit is contained in:
parent
b45f30c58a
commit
d1c6bcacd9
|
@ -0,0 +1,167 @@
|
|||
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "BattleChipModel.h"
|
||||
|
||||
#include "ConfigController.h"
|
||||
#include "GBAApp.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QMimeData>
|
||||
#include <QResource>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
BattleChipModel::BattleChipModel(QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
QResource::registerResource(GBAApp::dataDir() + "/chips.rcc", "/exe");
|
||||
QResource::registerResource(ConfigController::configDir() + "/chips.rcc", "/exe");
|
||||
}
|
||||
|
||||
int BattleChipModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_deck.count();
|
||||
}
|
||||
|
||||
QVariant BattleChipModel::data(const QModelIndex& index, int role) const {
|
||||
const BattleChip& item = m_deck[index.row()];
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return item.name;
|
||||
case Qt::DecorationRole:
|
||||
return item.icon;
|
||||
case Qt::UserRole:
|
||||
return item.id;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags BattleChipModel::flags(const QModelIndex& index) const {
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
|
||||
}
|
||||
|
||||
bool BattleChipModel::removeRows(int row, int count, const QModelIndex& parent) {
|
||||
if (parent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
beginRemoveRows(QModelIndex(), row, row + count - 1);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
m_deck.removeAt(row);
|
||||
}
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList BattleChipModel::mimeTypes() const {
|
||||
return {"text/plain"};
|
||||
}
|
||||
|
||||
Qt::DropActions BattleChipModel::supportedDropActions() const {
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
QMimeData* BattleChipModel::mimeData(const QModelIndexList& indices) const {
|
||||
QStringList deck;
|
||||
for (const QModelIndex& index : indices) {
|
||||
if (index.parent().isValid()) {
|
||||
continue;
|
||||
}
|
||||
deck.append(QString::number(m_deck[index.row()].id));
|
||||
}
|
||||
|
||||
QMimeData* mimeData = new QMimeData();
|
||||
mimeData->setData("text/plain", deck.join(',').toLocal8Bit());
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool BattleChipModel::dropMimeData(const QMimeData* data, Qt::DropAction, int row, int, const QModelIndex& parent) {
|
||||
if (parent.parent().isValid()) {
|
||||
return false;
|
||||
}
|
||||
QStringList deck = QString::fromLocal8Bit(data->data("text/plain")).split(',');
|
||||
if (deck.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
row = parent.row();
|
||||
beginInsertRows(QModelIndex(), row, row + deck.count() - 1);
|
||||
for (int i = 0; i < deck.count(); ++i) {
|
||||
int id = deck[i].toInt();
|
||||
m_deck.insert(row + i, createChip(id));
|
||||
}
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BattleChipModel::setFlavor(int flavor) {
|
||||
m_chipIdToName.clear();
|
||||
if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) {
|
||||
flavor = GBA_FLAVOR_BEAST_LINK_GATE;
|
||||
}
|
||||
m_flavor = flavor;
|
||||
|
||||
QFile file(QString(":/exe/exe%1/chip-names.txt").arg(flavor));
|
||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
int id = 0;
|
||||
while (true) {
|
||||
QByteArray line = file.readLine();
|
||||
if (line.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
++id;
|
||||
if (line.trimmed().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QString name = QString::fromUtf8(line).trimmed();
|
||||
m_chipIdToName[id] = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void BattleChipModel::addChip(int id) {
|
||||
beginInsertRows(QModelIndex(), m_deck.count(), m_deck.count());
|
||||
m_deck.append(createChip(id));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void BattleChipModel::removeChip(const QModelIndex& index) {
|
||||
beginRemoveRows(QModelIndex(), index.row(), index.row());
|
||||
m_deck.removeAt(index.row());
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void BattleChipModel::setChips(QList<int> ids) {
|
||||
beginResetModel();
|
||||
m_deck.clear();
|
||||
for (int id : ids) {
|
||||
m_deck.append(createChip(id));
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void BattleChipModel::clear() {
|
||||
beginResetModel();
|
||||
m_deck.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
BattleChipModel::BattleChip BattleChipModel::createChip(int id) const {
|
||||
QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
|
||||
if (!QFile(path).exists()) {
|
||||
path = QString(":/exe/exe%1/placeholder.png").arg(m_flavor);
|
||||
}
|
||||
QIcon icon(path);
|
||||
|
||||
BattleChip chip = {
|
||||
id,
|
||||
m_chipIdToName[id],
|
||||
icon
|
||||
};
|
||||
return chip;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* Copyright (c) 2013-2019 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QIcon>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class BattleChipModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BattleChipModel(QObject* parent = nullptr);
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
virtual Qt::DropActions supportedDropActions() const override;
|
||||
virtual QStringList mimeTypes() const override;
|
||||
virtual QMimeData* mimeData(const QModelIndexList& indices) const override;
|
||||
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction, int row, int column, const QModelIndex& parent) override;
|
||||
|
||||
int flavor() const { return m_flavor; }
|
||||
QMap<int, QString> chipNames() const { return m_chipIdToName; }
|
||||
|
||||
public slots:
|
||||
void setFlavor(int);
|
||||
void addChip(int id);
|
||||
void removeChip(const QModelIndex&);
|
||||
void setChips(QList<int> ids);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
struct BattleChip {
|
||||
int id;
|
||||
QString name;
|
||||
QIcon icon;
|
||||
};
|
||||
|
||||
BattleChip createChip(int id) const;
|
||||
|
||||
QMap<int, QString> m_chipIdToName;
|
||||
int m_flavor;
|
||||
|
||||
QList<BattleChip> m_deck;
|
||||
};
|
||||
|
||||
}
|
|
@ -5,18 +5,14 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "BattleChipView.h"
|
||||
|
||||
#include "ConfigController.h"
|
||||
#include "CoreController.h"
|
||||
#include "GBAApp.h"
|
||||
#include "ShortcutController.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <QtAlgorithms>
|
||||
#include <QFile>
|
||||
#include <QFontMetrics>
|
||||
#include <QMessageBox>
|
||||
#include <QMultiMap>
|
||||
#include <QResource>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
|
||||
|
@ -27,10 +23,8 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
, m_controller(controller)
|
||||
, m_window(window)
|
||||
{
|
||||
QResource::registerResource(GBAApp::dataDir() + "/chips.rcc", "/exe");
|
||||
QResource::registerResource(ConfigController::configDir() + "/chips.rcc", "/exe");
|
||||
|
||||
m_ui.setupUi(this);
|
||||
m_ui.chipList->setModel(&m_model);
|
||||
|
||||
char title[9];
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
|
@ -51,7 +45,7 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
m_ui.inserted->setChecked(Qt::Unchecked);
|
||||
});
|
||||
connect(m_ui.chipName, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui.chipId, [this](int id) {
|
||||
m_ui.chipId->setValue(m_chipIndexToId[id]);
|
||||
m_ui.chipId->setValue(m_model.chipNames().keys()[id]);
|
||||
});
|
||||
|
||||
connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);
|
||||
|
@ -61,7 +55,7 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
|
||||
connect(m_ui.save, &QAbstractButton::clicked, this, &BattleChipView::saveDeck);
|
||||
connect(m_ui.load, &QAbstractButton::clicked, this, &BattleChipView::loadDeck);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, m_ui.chipList, &QListWidget::clear);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, &m_model, &BattleChipModel::clear);
|
||||
|
||||
connect(m_ui.gateBattleChip, &QAbstractButton::toggled, this, [this](bool on) {
|
||||
if (on) {
|
||||
|
@ -85,13 +79,14 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
|
||||
connect(m_controller.get(), &CoreController::frameAvailable, this, &BattleChipView::advanceFrameCounter);
|
||||
|
||||
connect(m_ui.chipList, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) {
|
||||
QVariant chip = item->data(Qt::UserRole);
|
||||
connect(m_ui.chipList, &QAbstractItemView::clicked, this, [this](const QModelIndex& index) {
|
||||
QVariant chip = m_model.data(index, Qt::UserRole);
|
||||
bool blocked = m_ui.chipId->blockSignals(true);
|
||||
m_ui.chipId->setValue(chip.toInt());
|
||||
m_ui.chipId->blockSignals(blocked);
|
||||
reinsert();
|
||||
});
|
||||
connect(m_ui.chipList, &QListView::indexesMoved, this, &BattleChipView::resort);
|
||||
|
||||
m_controller->attachBattleChipGate();
|
||||
setFlavor(4);
|
||||
|
@ -110,7 +105,8 @@ BattleChipView::~BattleChipView() {
|
|||
|
||||
void BattleChipView::setFlavor(int flavor) {
|
||||
m_controller->setBattleChipFlavor(flavor);
|
||||
loadChipNames(flavor);
|
||||
m_model.setFlavor(flavor);
|
||||
m_ui.chipName->addItems(m_model.chipNames().values());
|
||||
}
|
||||
|
||||
void BattleChipView::insertChip(bool inserted) {
|
||||
|
@ -141,55 +137,13 @@ void BattleChipView::addChip() {
|
|||
if (insertedChip < 1) {
|
||||
return;
|
||||
}
|
||||
addChipId(insertedChip);
|
||||
}
|
||||
|
||||
void BattleChipView::addChipId(int id) {
|
||||
QListWidgetItem* add = new QListWidgetItem(m_chipIdToName[id]);
|
||||
add->setData(Qt::UserRole, id);
|
||||
QString path = QString(":/exe/exe%1/%2.png").arg(m_flavor).arg(id, 3, 10, QLatin1Char('0'));
|
||||
if (!QFile(path).exists()) {
|
||||
path = QString(":/exe/exe%1/placeholder.png").arg(m_flavor);
|
||||
}
|
||||
add->setIcon(QPixmap(path).scaled(m_ui.chipList->iconSize()));
|
||||
m_ui.chipList->addItem(add);
|
||||
m_model.addChip(insertedChip);
|
||||
}
|
||||
|
||||
void BattleChipView::removeChip() {
|
||||
qDeleteAll(m_ui.chipList->selectedItems());
|
||||
}
|
||||
|
||||
void BattleChipView::loadChipNames(int flavor) {
|
||||
QStringList chipNames;
|
||||
chipNames.append(tr("(None)"));
|
||||
|
||||
m_chipIndexToId.clear();
|
||||
m_chipIdToName.clear();
|
||||
if (flavor == GBA_FLAVOR_BEAST_LINK_GATE_US) {
|
||||
flavor = GBA_FLAVOR_BEAST_LINK_GATE;
|
||||
for (const auto& index : m_ui.chipList->selectionModel()->selectedIndexes()) {
|
||||
m_model.removeChip(index);
|
||||
}
|
||||
m_flavor = flavor;
|
||||
|
||||
QFile file(QString(":/exe/exe%1/chip-names.txt").arg(flavor));
|
||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
int id = 0;
|
||||
while (true) {
|
||||
QByteArray line = file.readLine();
|
||||
if (line.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
++id;
|
||||
if (line.trimmed().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QString name = QString::fromUtf8(line).trimmed();
|
||||
m_chipIndexToId[chipNames.length()] = id;
|
||||
m_chipIdToName[id] = name;
|
||||
chipNames.append(name);
|
||||
}
|
||||
|
||||
m_ui.chipName->clear();
|
||||
m_ui.chipName->addItems(chipNames);
|
||||
}
|
||||
|
||||
void BattleChipView::advanceFrameCounter() {
|
||||
|
@ -208,14 +162,14 @@ void BattleChipView::saveDeck() {
|
|||
}
|
||||
|
||||
QStringList deck;
|
||||
for (int i = 0; i < m_ui.chipList->count(); ++i) {
|
||||
deck.append(m_ui.chipList->item(i)->data(Qt::UserRole).toString());
|
||||
for (int i = 0; i < m_model.rowCount(); ++i) {
|
||||
deck.append(m_model.data(m_model.index(i, 0), Qt::UserRole).toString());
|
||||
}
|
||||
|
||||
QSettings ini(filename, QSettings::IniFormat);
|
||||
ini.clear();
|
||||
ini.beginGroup("BattleChipDeck");
|
||||
ini.setValue("version", m_flavor);
|
||||
ini.setValue("version", m_model.flavor());
|
||||
ini.setValue("deck", deck.join(','));
|
||||
ini.sync();
|
||||
}
|
||||
|
@ -229,7 +183,7 @@ void BattleChipView::loadDeck() {
|
|||
QSettings ini(filename, QSettings::IniFormat);
|
||||
ini.beginGroup("BattleChipDeck");
|
||||
int flavor = ini.value("version").toInt();
|
||||
if (flavor != m_flavor) {
|
||||
if (flavor != m_model.flavor()) {
|
||||
QMessageBox* error = new QMessageBox(this);
|
||||
error->setIcon(QMessageBox::Warning);
|
||||
error->setStandardButtons(QMessageBox::Ok);
|
||||
|
@ -240,13 +194,30 @@ void BattleChipView::loadDeck() {
|
|||
return;
|
||||
}
|
||||
|
||||
m_ui.chipList->clear();
|
||||
QList<int> newDeck;
|
||||
QStringList deck = ini.value("deck").toString().split(',');
|
||||
for (const auto& item : deck) {
|
||||
bool ok;
|
||||
int id = item.toInt(&ok);
|
||||
if (ok) {
|
||||
addChipId(id);
|
||||
newDeck.append(id);
|
||||
}
|
||||
}
|
||||
m_model.setChips(newDeck);
|
||||
}
|
||||
|
||||
void BattleChipView::resort() {
|
||||
QMap<int, int> chips;
|
||||
for (int i = 0; i < m_model.rowCount(); ++i) {
|
||||
QModelIndex index = m_model.index(i, 0);
|
||||
QRect visualRect = m_ui.chipList->visualRect(index);
|
||||
QSize gridSize = m_ui.chipList->gridSize();
|
||||
int x = visualRect.y() / gridSize.height();
|
||||
x *= m_ui.chipList->viewport()->width();
|
||||
x += visualRect.x();
|
||||
x *= m_model.rowCount();
|
||||
x += index.row();
|
||||
chips[x] = m_model.data(index, Qt::UserRole).toInt();
|
||||
}
|
||||
m_model.setChips(chips.values());
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include "BattleChipModel.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
@ -29,11 +31,11 @@ public slots:
|
|||
void setFlavor(int);
|
||||
void insertChip(bool);
|
||||
void reinsert();
|
||||
void resort();
|
||||
|
||||
private slots:
|
||||
void advanceFrameCounter();
|
||||
void addChip();
|
||||
void addChipId(int);
|
||||
void removeChip();
|
||||
|
||||
void saveDeck();
|
||||
|
@ -46,10 +48,8 @@ private:
|
|||
|
||||
Ui::BattleChipView m_ui;
|
||||
|
||||
QMap<int, int> m_chipIndexToId;
|
||||
QMap<int, QString> m_chipIdToName;
|
||||
BattleChipModel m_model;
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
int m_flavor;
|
||||
|
||||
int m_frameCounter = -1;
|
||||
bool m_next = false;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0,0,0">
|
||||
<item>
|
||||
<widget class="QListWidget" name="chipList">
|
||||
<widget class="QListView" name="chipList">
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -35,7 +35,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
<enum>QListView::Free</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
|
@ -52,12 +52,6 @@
|
|||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionRectVisible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -132,9 +126,9 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="advanced" native="true">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
|
@ -260,21 +254,5 @@
|
|||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>chipList</sender>
|
||||
<signal>indexesMoved(QModelIndexList)</signal>
|
||||
<receiver>chipList</receiver>
|
||||
<slot>doItemsLayout()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>314</x>
|
||||
<y>203</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>203</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
|
@ -147,6 +147,7 @@ set(UI_FILES
|
|||
VideoView.ui)
|
||||
|
||||
set(GBA_SRC
|
||||
BattleChipModel.cpp
|
||||
BattleChipView.cpp
|
||||
GBAOverride.cpp)
|
||||
|
||||
|
|
Loading…
Reference in New Issue