diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..4cb930b9 --- /dev/null +++ b/.clangd @@ -0,0 +1,3 @@ +CompileFlags: + Add: [-W3] + Remove: [-fsanitize*, -mno-direct-extern-access] diff --git a/CMakeLists.txt b/CMakeLists.txt index bf23f413..8c2b2db6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,14 +324,17 @@ set(EDITOR_SOURCES set(EDITOR_QML_UI src/ui/common/CheckBox.qml + src/ui/common/ComboBox.qml + src/ui/common/RadioButton.qml src/ui/common/SpinBox.qml src/ui/common/TextField.qml src/ui/MainWindow.qml - src/ui/database/DatabaseWindow.qml src/ui/database/ActorPage.qml + src/ui/database/AttributePage.qml src/ui/database/DatabaseEntryListPage.qml src/ui/database/DatabaseEntryPage.qml src/ui/database/DatabasePage.qml + src/ui/database/DatabaseWindow.qml src/ui/database/ItemPage.qml src/ui/database/SkillPage.qml src/ui/database/VocabularyPage.qml diff --git a/src/common/lcf_widget_binding.h b/src/common/lcf_widget_binding.h index 0921e805..bda5adae 100644 --- a/src/common/lcf_widget_binding.h +++ b/src/common/lcf_widget_binding.h @@ -79,7 +79,7 @@ namespace LcfWidgetBinding { data.obj() = static_cast(checkBox->isChecked()); }; - QWidget::connect(checkBox, &QCheckBox::stateChanged, parent, callback); + QWidget::connect(checkBox, &QCheckBox::checkStateChanged, parent, callback); } template diff --git a/src/common/sortfilter_proxy_models.cpp b/src/common/sortfilter_proxy_models.cpp index b094ba6a..6d2e975e 100644 --- a/src/common/sortfilter_proxy_models.cpp +++ b/src/common/sortfilter_proxy_models.cpp @@ -16,16 +16,17 @@ */ #include "sortfilter_proxy_models.h" +#include "json_list_view.h" -SortFilterProxyModelIdFilter::SortFilterProxyModelIdFilter(const std::vector& indices) : - QSortFilterProxyModel() { +SortFilterProxyModelIdFilter::SortFilterProxyModelIdFilter(const std::vector& indices, QObject* parent) : + QSortFilterProxyModel(parent) { this->indices = indices; std::sort(this->indices.begin(), this->indices.end()); } bool SortFilterProxyModelIdFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - int val = sourceModel()->data(index, Qt::UserRole).toInt(); + int val = sourceModel()->data(index, JsonListView::IdRole).toInt(); return std::binary_search(this->indices.begin(), this->indices.end(), val); } diff --git a/src/common/sortfilter_proxy_models.h b/src/common/sortfilter_proxy_models.h index e5b840a3..79ded918 100644 --- a/src/common/sortfilter_proxy_models.h +++ b/src/common/sortfilter_proxy_models.h @@ -28,8 +28,10 @@ * Filters by a list of Lcf IDs */ class SortFilterProxyModelIdFilter : public QSortFilterProxyModel { + Q_OBJECT + public: - SortFilterProxyModelIdFilter(const std::vector& indices); + SortFilterProxyModelIdFilter(const std::vector& indices, QObject* parent); bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; private: diff --git a/src/model/actor.cpp b/src/model/actor.cpp index 5282ff63..64c10b26 100644 --- a/src/model/actor.cpp +++ b/src/model/actor.cpp @@ -17,24 +17,25 @@ #include "actor.h" #include +#include "common/image_loader.h" #include "ui/database/actor_widget.h" #include "common/dbstring.h" #include "common/sortfilter_proxy_models.h" ActorModel::ActorModel(ProjectData& project, lcf::rpg::Actor& data) : - RpgBase(project), m_data(data) { + RpgBase(project), m_data(&data) { } lcf::rpg::Actor& ActorModel::data() { - return m_data; + return *m_data; } QPixmap ActorModel::preview() { - QString path = m_project.project().findFile("FaceSet", ToQString(m_data.face_name), FileFinder::FileType::Image); + QString path = m_project->project().findFile("FaceSet", ToQString(m_data->face_name), FileFinder::FileType::Image); if (!path.isEmpty()) { QPixmap faceSet = ImageLoader::Load(path); - int x = (m_data.face_index % 4) * 48; - int y = (m_data.face_index / 4) * 48; + int x = (m_data->face_index % 4) * 48; + int y = (m_data->face_index / 4) * 48; return faceSet.copy(x, y, 48, 48); } else { @@ -46,11 +47,11 @@ QPixmap ActorModel::preview() { } const lcf::rpg::Actor& ActorModel::data() const { - return m_data; + return *m_data; } bool ActorModel::IsItemUsable(const lcf::rpg::Item& item) const { - int query_idx = m_data.ID - 1; + int query_idx = m_data->ID - 1; auto* query_set = &item.actor_set; /*TODO if (Player::IsRPG2k3() && Data::system.equipment_setting == lcf::rpg::System::EquipmentSetting_class) { auto* cls = GetClass(); @@ -69,10 +70,10 @@ bool ActorModel::IsItemUsable(const lcf::rpg::Item& item) const { return (*query_set)[query_idx]; } -QSortFilterProxyModel* ActorModel::CreateEquipmentFilter(lcf::rpg::Item::Type type) { - std::vector indices; +QAbstractItemModel* ActorModel::CreateEquipmentFilter(lcf::rpg::Item::Type type, QObject* parent) { + std::vector indices = {0}; // Include (None) - for (const auto& item : m_project.database().items) { + for (const auto& item : m_project->database().items) { if (item.type != type || !IsItemUsable(item)) { continue; } @@ -80,5 +81,11 @@ QSortFilterProxyModel* ActorModel::CreateEquipmentFilter(lcf::rpg::Item::Type ty indices.push_back(item.ID); } - return new SortFilterProxyModelIdFilter(indices); + auto filter = new SortFilterProxyModelIdFilter(indices, parent); + + if (!parent) { + QQmlEngine::setObjectOwnership(filter, QQmlEngine::JavaScriptOwnership); + } + + return filter; } diff --git a/src/model/actor.h b/src/model/actor.h index aeac9107..37cc3c6f 100644 --- a/src/model/actor.h +++ b/src/model/actor.h @@ -20,17 +20,20 @@ #include #include #include -#include "project.h" #include "rpg_base.h" -class QSortFilterProxyModel; +class QAbstractItemModel; +class ProjectData; /** * A thin wrapper around lcf::rpg::Actor */ class ActorModel : public RpgBase { + Q_GADGET + public: + ActorModel() = default; ActorModel(ProjectData& project, lcf::rpg::Actor& data); bool IsItemUsable(const lcf::rpg::Item& item) const; @@ -41,7 +44,7 @@ class ActorModel : public RpgBase * @param type Equipment type * @return QSortFilterProxyModel */ - QSortFilterProxyModel* CreateEquipmentFilter(lcf::rpg::Item::Type type); + Q_INVOKABLE QAbstractItemModel* CreateEquipmentFilter(lcf::rpg::Item::Type type, QObject* parent = nullptr); lcf::rpg::Actor& data(); @@ -54,6 +57,6 @@ class ActorModel : public RpgBase QPixmap preview() override; private: - lcf::rpg::Actor& m_data; + lcf::rpg::Actor* m_data = nullptr; }; diff --git a/src/model/enemy.cpp b/src/model/enemy.cpp index a29d528f..e3cde946 100644 --- a/src/model/enemy.cpp +++ b/src/model/enemy.cpp @@ -16,6 +16,10 @@ */ #include "enemy.h" +#include "common/filefinder.h" +#include "common/image_loader.h" +#include "model/project.h" +#include "model/project_data.h" #include "ui/database/enemy_widget.h" #include "common/dbstring.h" @@ -29,7 +33,7 @@ lcf::rpg::Enemy& EnemyModel::data() { } QPixmap EnemyModel::preview() { - QPixmap monster = ImageLoader::Load(m_project.project().findFile("Monster", ToQString(data().battler_name), FileFinder::FileType::Image)); + QPixmap monster = ImageLoader::Load(m_project->project().findFile("Monster", ToQString(data().battler_name), FileFinder::FileType::Image)); if (!monster) { return QPixmap(48, 48); } diff --git a/src/model/project.cpp b/src/model/project.cpp index f617e726..b8e07867 100644 --- a/src/model/project.cpp +++ b/src/model/project.cpp @@ -69,11 +69,11 @@ std::shared_ptr Project::load(const QDir& dir) { if (!cfg.isNull()) { lcf::INIReader ini(cfg.toStdString()); - auto title = ini.GetString("RPG_RT", GAMETITLE, tr("Untitled").toStdString()); + std::string title = std::string(ini.GetString("RPG_RT", GAMETITLE, tr("Untitled").toStdString())); if (project_type == FileFinder::ProjectType::Legacy) { // Check for game encoding - auto enc = ini.GetString("EasyRPG", "Encoding", ""); + std::string enc = std::string(ini.GetString("EasyRPG", "Encoding", "")); if (enc.empty()) { // Only use the title for encoding detection // This is called for all games in the "Open Project" list diff --git a/src/model/rpg_base.cpp b/src/model/rpg_base.cpp index e281c335..87e2d89a 100644 --- a/src/model/rpg_base.cpp +++ b/src/model/rpg_base.cpp @@ -18,6 +18,6 @@ #include "rpg_base.h" -RpgBase::RpgBase(ProjectData &project) : m_project(project) { +RpgBase::RpgBase(ProjectData &project) : m_project(&project) { } diff --git a/src/model/rpg_base.h b/src/model/rpg_base.h index 41060e5a..bc42e560 100644 --- a/src/model/rpg_base.h +++ b/src/model/rpg_base.h @@ -19,12 +19,14 @@ #include #include -#include "common/image_loader.h" -#include "project.h" -#include "core.h" + +class ProjectData; class RpgBase { + Q_GADGET + public: + RpgBase() = default; explicit RpgBase(ProjectData& project); virtual QPixmap preview() { @@ -32,9 +34,9 @@ class RpgBase { } ProjectData& project() const { - return m_project; + return *m_project; } protected: - ProjectData& m_project; + ProjectData* m_project = nullptr; }; diff --git a/src/qmlbinding/json.h b/src/qmlbinding/json.h index 625ca090..0f0f0fb9 100644 --- a/src/qmlbinding/json.h +++ b/src/qmlbinding/json.h @@ -29,6 +29,7 @@ class Json : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("Json is an abstract base class") public: explicit Json(QObject* parent = nullptr) : QObject(parent) {} diff --git a/src/qmlbinding/json_internals/json_t_impl.h b/src/qmlbinding/json_internals/json_t_impl.h index bfd53a77..d1a2a62c 100644 --- a/src/qmlbinding/json_internals/json_t_impl.h +++ b/src/qmlbinding/json_internals/json_t_impl.h @@ -47,10 +47,22 @@ QString JsonT::str(QString jsonPtr) const { template int JsonT::num(QString jsonPtr) const { - auto res = glz::get(*m_data, jsonPtr.toStdString()); + std::string ptr = jsonPtr.toStdString(); - if (res) { - return res.value(); + if (auto res = glz::get(*m_data, ptr)) { + return res.value().get(); + } + + if (auto res = glz::get(*m_data, ptr)) { + return static_cast(res.value().get()); + } + + if (auto res = glz::get(*m_data, ptr)) { + return static_cast(res.value().get()); + } + + if (auto res = glz::get(*m_data, ptr)) { + return static_cast(res.value().get()); } qDebug() << "Json::num: Not pointing to int: " << jsonPtr; @@ -77,6 +89,9 @@ void JsonT::set(QString jsonPtr, const QVariant& value) { case QMetaType::Int: glz::set(m_data, jsonPtr.toStdString(), value.toInt()); break; + case QMetaType::Double: + glz::set(m_data, jsonPtr.toStdString(), value.toDouble()); + break; case QMetaType::QString: { lcf::DBString s = ToDBString(value.value()); glz::set(m_data, jsonPtr.toStdString(), s); @@ -87,14 +102,14 @@ void JsonT::set(QString jsonPtr, const QVariant& value) { break; } default: - assert(false); + qDebug() << "Json::set: Type unsupported: " << value.typeName(); break; } } template concept IsVector = requires { - typename T::value_type; + typename T::value_type; } && std::is_class_v; template diff --git a/src/qmlbinding/json_list_view.cpp b/src/qmlbinding/json_list_view.cpp index 8b5cfbe4..369ec121 100644 --- a/src/qmlbinding/json_list_view.cpp +++ b/src/qmlbinding/json_list_view.cpp @@ -19,11 +19,11 @@ QHash JsonListView::roleNames() const { QHash roles; - roles[Qt::DisplayRole] = "display"; + roles[Qt::DisplayRole] = "text"; roles[NameRole] = "name"; roles[TitleRole] = "title"; - roles[IdRole] = "index"; - roles[IndexRole] = "listindex"; + roles[IdRole] = "value"; + roles[IndexRole] = "index"; return roles; } diff --git a/src/qmlbinding/json_list_view.h b/src/qmlbinding/json_list_view.h index 71f57e53..5970297e 100644 --- a/src/qmlbinding/json_list_view.h +++ b/src/qmlbinding/json_list_view.h @@ -28,6 +28,8 @@ class JsonListView : public QAbstractListModel { Q_OBJECT QML_ELEMENT + Q_PROPERTY(int fallbackValue MEMBER m_fallbackValue NOTIFY fallbackValueChanged) + Q_PROPERTY(QString fallbackString MEMBER m_fallbackString NOTIFY fallbackStringChanged) public: enum RoleNames { @@ -59,11 +61,19 @@ class JsonListView : public QAbstractListModel { JsonView* view() const { return m_view; } void setView(JsonView* view) { m_view = view; } + bool hasFallback() const { + return !m_fallbackString.isEmpty(); + } + signals: void dataChanged(); + void fallbackValueChanged(); + void fallbackStringChanged(); protected: JsonView* m_view = nullptr; + int m_fallbackValue = 0; + QString m_fallbackString; }; template @@ -82,14 +92,13 @@ class JsonListViewT : public JsonListView { std::vector* data() const { return m_data; } void setData(std::vector* data) { m_data = data; } - private: std::vector* m_data = nullptr; }; template inline int JsonListViewT::rowCount(const QModelIndex &parent) const { - return m_data->size(); + return m_data->size() + (hasFallback() ? 1 : 0); } template @@ -98,7 +107,6 @@ inline QVariant JsonListViewT::data(const QModelIndex &index, int role) return QVariant(); } - QVariant value; std::string role_str; switch (role) { @@ -116,27 +124,43 @@ inline QVariant JsonListViewT::data(const QModelIndex &index, int role) case Qt::DisplayRole: { auto id = data(index, IdRole); auto name = data(index, NameRole); - auto s = QString("%1: %2").arg(id.toInt(), 4, u'0').arg(name.toString()); - qDebug() << s; + auto s = QString("%1: %2").arg(id.toInt(), 4, 10, u'0').arg(name.toString()); return QVariant::fromValue(s); } default: return QVariant(); } - role_str = std::format("/{}/{}", index.row(), role_str); + if (hasFallback() && index.row() == 0) { + switch (role) { + case NameRole: + return QVariant::fromValue(m_fallbackString); + case IdRole: + return QVariant::fromValue(m_fallbackValue); + default: + return {}; + } + } + + // Shift row back in case of fallback + int row = index.row(); + if (hasFallback()) { + --row; + } + + role_str = std::format("/{}/{}", row, role_str); auto res = glz::get(*m_data, role_str); if (res.has_value()) { - value = QVariant::fromValue(ToQString(res.value())); + return QVariant::fromValue(ToQString(res.value())); } auto res2 = glz::get(*m_data, role_str); if (res2.has_value()) { - value = QVariant::fromValue(res2.value()); + return QVariant::fromValue(res2.value().get()); } - return value; + return {}; } template diff --git a/src/qmlbinding/project_data_gadget.cpp b/src/qmlbinding/project_data_gadget.cpp index 6f02d742..930a3720 100644 --- a/src/qmlbinding/project_data_gadget.cpp +++ b/src/qmlbinding/project_data_gadget.cpp @@ -16,6 +16,7 @@ */ #include "project_data_gadget.h" +#include "model/actor.h" #include "model/project.h" #include "json_view.h" @@ -37,9 +38,6 @@ void ProjectDataGadget::setProjectData(ProjectData* data) { } JsonView* ProjectDataGadget::database() { - auto res = m_database_json.subtree("/"); - qDebug() << "database() " << m_data << " " << res; - return m_data ? qvariant_cast(m_database_json.subtree("/")) : nullptr; } @@ -67,6 +65,10 @@ QString ProjectDataGadget::findDirectory(const QString& baseDir, const QString& return m_data->project().findDirectory(baseDir, dir); } +ActorModel ProjectDataGadget::actorModel(int actor_index) { + return ActorModel(*m_data, m_data->database().actors[actor_index]); +} + QString ProjectDataGadget::projectPath() const { return m_data->project().projectDir().absolutePath(); } diff --git a/src/qmlbinding/project_data_gadget.h b/src/qmlbinding/project_data_gadget.h index 1993d4ce..3c585a4d 100644 --- a/src/qmlbinding/project_data_gadget.h +++ b/src/qmlbinding/project_data_gadget.h @@ -23,6 +23,7 @@ #include #include +class ActorModel; class JsonView; /** @@ -49,6 +50,8 @@ class ProjectDataGadget : public QObject Q_INVOKABLE QString findDirectory(const QString& dir) const; Q_INVOKABLE QString findDirectory(const QString& baseDir, const QString& dir) const; + Q_INVOKABLE ActorModel actorModel(int actor_index); + QString projectPath() const; signals: diff --git a/src/ui/common/CheckBox.qml b/src/ui/common/CheckBox.qml index 8d040c7b..34ffa7e9 100644 --- a/src/ui/common/CheckBox.qml +++ b/src/ui/common/CheckBox.qml @@ -12,9 +12,8 @@ Controls.CheckBox { property Ez.JsonView jsonData onToggled: { - //console.log("Text changed to:", text) if (jsonData !== null && key !== "") { - jsonData.set(key, checkState); + jsonData.set(key, checked); } } @@ -23,6 +22,10 @@ Controls.CheckBox { } function onDataChanged() { - checkState = (jsonData.boolean(key)); + if (jsonData !== null && key !== "") { + checked = jsonData.boolean(key); + } else { + checked = false; + } } } diff --git a/src/ui/common/ComboBox.qml b/src/ui/common/ComboBox.qml new file mode 100644 index 00000000..8fb908e8 --- /dev/null +++ b/src/ui/common/ComboBox.qml @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: EasyRPG Editor Authors +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Controls as Controls +import org.easyrpg.editor as Ez + +Controls.ComboBox { + id: root + + property string key + property var jsonData + + textRole: "text" // In a JsonListView this is "ID: NAME", e.g. "0001: Aina" + valueRole: "value" // In a JsonListView is the "ID" + + onActivated: { + if (jsonData !== null && key !== "") { + jsonData.set(key, currentValue) + } + } + + Component.onCompleted: { + onDataChanged() + } + + function onDataChanged() { + if (jsonData !== null && key !== "") { + currentIndex = indexOfValue(jsonData.num(key)) + if (currentIndex === -1) { + console.log(`ComboBox: ${jsonData.num(key)} not found for ${key}`); + + let isProxy = (root.model.sourceModel !== undefined); + let model = isProxy ? root.model.sourceModel : root.model; + + if (model.fallbackString !== "") { + currentIndex = indexOfValue(model.fallbackValue); + } + } + } + } +} diff --git a/src/ui/common/RadioButton.qml b/src/ui/common/RadioButton.qml new file mode 100644 index 00000000..806f4692 --- /dev/null +++ b/src/ui/common/RadioButton.qml @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: EasyRPG Editor Authors +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Controls as Controls +import org.easyrpg.editor as Ez + +Controls.RadioButton { + id: root + + property string key + property Ez.JsonView jsonData + + property int value + + onCheckedChanged: { + if (checked && jsonData !== null && key !== "") { + jsonData.set(key, value) + } + } + + Component.onCompleted: { + onDataChanged() + } + + function onDataChanged() { + checked = (jsonData.num(key) === value) + } +} diff --git a/src/ui/common/SpinBox.qml b/src/ui/common/SpinBox.qml index 14b834b3..d71c316a 100644 --- a/src/ui/common/SpinBox.qml +++ b/src/ui/common/SpinBox.qml @@ -11,6 +11,9 @@ Controls.SpinBox { property string key property Ez.JsonView jsonData + property string prefix: "" + property string suffix: "" + onValueChanged: { if (jsonData !== null && key !== "") { jsonData.set(key, value) @@ -24,4 +27,20 @@ Controls.SpinBox { function onDataChanged() { value = jsonData.num(key) } + + textFromValue: function(value, locale) { + return prefix + Number(value).toLocaleString(locale, 'f', 0) + suffix; + } + + valueFromText: function(text, locale) { + if (prefix !== "" && text.startsWith(prefix)) { + text = text.substring(prefix.length); + } + + if (suffix !== "" && text.endsWith(suffix)) { + text = text.substring(0, text.length - suffix.length); + } + + return Number.fromLocaleString(locale, text); + } } diff --git a/src/ui/common/rpg_model.h b/src/ui/common/rpg_model.h index 1dbf5b8c..82938f9a 100644 --- a/src/ui/common/rpg_model.h +++ b/src/ui/common/rpg_model.h @@ -26,6 +26,7 @@ #include "common/dbstring.h" #include "common/filefinder.h" #include "common/image_loader.h" +#include "json_list_view.h" #include "model/rpg_reflect.h" template @@ -62,7 +63,7 @@ QVariant RpgModel::data(const QModelIndex &index, int role) const { return QString("%1: %2").arg(data.ID, 4, 10, QChar('0')).arg(ToQString(data.name)); } else if (role == Qt::DecorationRole) { return typename RpgReflect::model_type(m_project, m_data[index.row()]).preview(); - } else if (role == Qt::UserRole) { + } else if (role == Qt::UserRole || role == JsonListView::IdRole) { return m_data[index.row()].ID; } else if (role == ModelDataObject) { return QVariant::fromValue(&m_data[index.row()]); diff --git a/src/ui/common/system_color_combobox.cpp b/src/ui/common/system_color_combobox.cpp index 2c537b08..ccfa6383 100644 --- a/src/ui/common/system_color_combobox.cpp +++ b/src/ui/common/system_color_combobox.cpp @@ -16,6 +16,7 @@ */ #include "system_color_combobox.h" +#include "common/image_loader.h" SystemColorComboBox::SystemColorComboBox(QWidget *parent) : QComboBox(parent) {} diff --git a/src/ui/database/ActorPage.qml b/src/ui/database/ActorPage.qml index 1b1e1eee..8c0eaa58 100644 --- a/src/ui/database/ActorPage.qml +++ b/src/ui/database/ActorPage.qml @@ -12,6 +12,15 @@ import org.easyrpg.editor as Ez DatabaseEntryPage { id: root + Models.ListModel { + id: equipmentModel + Models.ListElement { key: "weapon_id"; label: "Weapon:"; type: 1 } + Models.ListElement { key: "shield_id"; label: "Shield:"; type: 2 } + Models.ListElement { key: "armor_id"; label: "Armor:"; type: 3 } + Models.ListElement { key: "helmet_id"; label: "Helmet:"; type: 4 } + Models.ListElement { key: "accessory_id"; label: "Accessory:"; type: 5 } + } + Kirigami.FormLayout { anchors.fill: parent @@ -68,5 +77,30 @@ DatabaseEntryPage { enabled: critical_hit_cb.checked } } + + Kirigami.Separator { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Equipment" + } + + Repeater { + id: equipmentRepeater + model: equipmentModel + + Ez.ComboBox { + readonly property var repdata: equipmentRepeater.model.get(index) + + jsonData: root.jsonData + key: "initial_equipment/" + repdata.key + Kirigami.FormData.label: repdata.label + model: { + let filter = Ez.ProjectData.actorModel(root.objIndex).CreateEquipmentFilter(repdata.type); + let list = Ez.ProjectData.database().list("items"); + list.fallbackString = "(None)"; + filter.sourceModel = list; + return filter; + } + } + } } } diff --git a/src/ui/database/AttributePage.qml b/src/ui/database/AttributePage.qml new file mode 100644 index 00000000..edf7c230 --- /dev/null +++ b/src/ui/database/AttributePage.qml @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: EasyRPG Editor Authors +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Controls +import QtQuick.Dialogs as Dialogs +import QtQml.Models as Models +import org.kde.kirigami as Kirigami +import org.easyrpg.editor as Ez + +DatabaseEntryPage { + id: root + + Models.ListModel { + id: rateModel + Models.ListElement { key: "a_rate"; label: "A Rate:" } + Models.ListElement { key: "b_rate"; label: "B Rate:" } + Models.ListElement { key: "c_rate"; label: "C Rate:" } + Models.ListElement { key: "d_rate"; label: "D Rate:" } + Models.ListElement { key: "e_rate"; label: "E Rate:" } + } + + Kirigami.FormLayout { + anchors.fill: parent + + Ez.TextField { + jsonData: root.jsonData + key: "name" + Kirigami.FormData.label: "Name:" + } + + ColumnLayout { + Kirigami.FormData.label: "Attribute Type:" + Kirigami.FormData.buddyFor: radio_physical + Ez.RadioButton { + id: radio_physical + jsonData: root.jsonData + key: "type" + text: "Physical" + value: 0 + } + Ez.RadioButton { + jsonData: root.jsonData + key: "type" + text: "Magical" + value: 1 + } + } + + Kirigami.Separator { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Damage Multipliers" + } + + Repeater { + model: rateModel + + Ez.SpinBox { + jsonData: root.jsonData + key: model.key + Kirigami.FormData.label: model.label + from: -9999 + to: 9999 + suffix: "%" + } + } + } +} diff --git a/src/ui/database/DatabaseEntryListPage.qml b/src/ui/database/DatabaseEntryListPage.qml index 9b0b983d..6161fdea 100644 --- a/src/ui/database/DatabaseEntryListPage.qml +++ b/src/ui/database/DatabaseEntryListPage.qml @@ -33,17 +33,17 @@ Kirigami.ScrollablePage { } delegate: Controls.ItemDelegate { - required property int listindex + required property int index required property string name width: ListView.view.width - text: (listindex+1).toString().padStart(4, '0') + ": " + name + text: (index+1).toString().padStart(4, '0') + ": " + name - highlighted: entryList.currentIndex === listindex + highlighted: entryList.currentIndex === index action: Controls.Action { onTriggered: { - entryList.currentIndex = listindex + entryList.currentIndex = index } } } @@ -65,7 +65,8 @@ Kirigami.ScrollablePage { //console.log("Pushing:", root.targetPage); pageStack.push(Qt.resolvedUrl(root.targetPage), { // Use the index passed to the function - jsonData: jsonData.subtree("/" + index) + jsonData: jsonData.subtree("/" + index), + objIndex: index }); } } diff --git a/src/ui/database/DatabaseEntryPage.qml b/src/ui/database/DatabaseEntryPage.qml index ba98ae01..f5523e74 100644 --- a/src/ui/database/DatabaseEntryPage.qml +++ b/src/ui/database/DatabaseEntryPage.qml @@ -18,6 +18,9 @@ Kirigami.ScrollablePage { Kirigami.ColumnView.fillWidth: true Kirigami.ColumnView.reservedSpace: applicationWindow().pageStack.defaultColumnWidth * (applicationWindow().pageStack.depth - 1) + /** When the object comes from a list, contains the index. Otherwise -1. */ + property int objIndex: -1 + // TODO: Not used. Just for testing property bool showActions: false diff --git a/src/ui/database/DatabasePage.qml b/src/ui/database/DatabasePage.qml index 7fca014c..4d21926f 100644 --- a/src/ui/database/DatabasePage.qml +++ b/src/ui/database/DatabasePage.qml @@ -41,6 +41,11 @@ Kirigami.ScrollablePage { key: "skills" targetPage: "SkillPage.qml" } + ListElement { + name: "Attributes" + key: "attributes" + targetPage: "AttributePage.qml" + } ListElement { name: "Vocabulary" key: "terms" diff --git a/src/ui/database/SkillPage.qml b/src/ui/database/SkillPage.qml index 632da10a..63b9f091 100644 --- a/src/ui/database/SkillPage.qml +++ b/src/ui/database/SkillPage.qml @@ -4,25 +4,42 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls as Controls +import QtQml.Models as Models import org.kde.kirigami as Kirigami import org.easyrpg.editor as Ez DatabaseEntryPage { id: root + Models.ListModel { + id: typeModel + Models.ListElement { value: 0; text: "Normal" } + Models.ListElement { value: 1; text: "Teleport" } + Models.ListElement { value: 2; text: "Escape" } + Models.ListElement { value: 3; text: "Switch" } + } + Kirigami.FormLayout { anchors.fill: parent - Ez.TextField { - jsonData: root.jsonData - key: "name" - Kirigami.FormData.label: "Name:" - } + Ez.TextField { + jsonData: root.jsonData + key: "name" + Kirigami.FormData.label: "Name:" + } + + Ez.TextField { + jsonData: root.jsonData + key: "description" + Kirigami.FormData.label: "Description:" + } - Ez.TextField { - jsonData: root.jsonData - key: "description" - Kirigami.FormData.label: "Description:" - } + Ez.ComboBox { + id: cbType + jsonData: root.jsonData + key: "type" + Kirigami.FormData.label: "Type: " + model: typeModel + } } } diff --git a/src/ui/database/actor_widget.cpp b/src/ui/database/actor_widget.cpp index 0a7e1e1f..9a18f118 100644 --- a/src/ui/database/actor_widget.cpp +++ b/src/ui/database/actor_widget.cpp @@ -241,7 +241,7 @@ void ActorWidget::on_currentActorChanged(lcf::rpg::Actor *actor) auto equipFilter = [&](auto& cbox, auto type) { SignalBlocker s(cbox->comboBox()); - cbox->setFilter(ActorModel(m_project, *m_current).CreateEquipmentFilter(type)); + cbox->setFilter(static_cast(ActorModel(m_project, *m_current).CreateEquipmentFilter(type, this))); }; equipFilter(ui->comboInitialWeapon, lcf::rpg::Item::Type_weapon);