From 8ba4d0ba6fa7979372d2362a331889f9c4ed325d Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 13 May 2025 16:03:27 -0400 Subject: [PATCH] Create a proper ObjectListModel Eventually we want to load multiple LGBs at once (and categorize objects further, of course) so we need an actual tree model. --- apps/mapeditor/CMakeLists.txt | 4 +- apps/mapeditor/include/objectlistmodel.h | 49 ++++++++ apps/mapeditor/include/objectlistwidget.h | 11 +- apps/mapeditor/src/objectlistmodel.cpp | 132 ++++++++++++++++++++++ apps/mapeditor/src/objectlistwidget.cpp | 40 ++----- 5 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 apps/mapeditor/include/objectlistmodel.h create mode 100644 apps/mapeditor/src/objectlistmodel.cpp diff --git a/apps/mapeditor/CMakeLists.txt b/apps/mapeditor/CMakeLists.txt index 086c689..9bc312a 100644 --- a/apps/mapeditor/CMakeLists.txt +++ b/apps/mapeditor/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(novus-mapeditor include/objectpass.h include/objectlistwidget.h include/appstate.h + include/objectlistmodel.h src/main.cpp src/mainwindow.cpp @@ -17,7 +18,8 @@ target_sources(novus-mapeditor src/mapview.cpp src/objectpass.cpp src/objectlistwidget.cpp - src/appstate.cpp) + src/appstate.cpp + src/objectlistmodel.cpp) target_include_directories(novus-mapeditor PUBLIC include) diff --git a/apps/mapeditor/include/objectlistmodel.h b/apps/mapeditor/include/objectlistmodel.h new file mode 100644 index 0000000..bd68d21 --- /dev/null +++ b/apps/mapeditor/include/objectlistmodel.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +class AppState; + +enum class TreeType { + /// Root of the tree + Root, + /// LGB file + File, + /// A single object + Object, +}; + +struct TreeInformation { + TreeType type; + TreeInformation *parent = nullptr; + int row = 0; + QString name; + + std::vector children; +}; + +class ObjectListModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit ObjectListModel(AppState *appState, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + +private: + void refresh(); + + AppState *m_appState = nullptr; + TreeInformation *m_rootItem = nullptr; +}; diff --git a/apps/mapeditor/include/objectlistwidget.h b/apps/mapeditor/include/objectlistwidget.h index 30401ec..2873e2a 100644 --- a/apps/mapeditor/include/objectlistwidget.h +++ b/apps/mapeditor/include/objectlistwidget.h @@ -3,10 +3,11 @@ #pragma once -#include +#include #include #include +class ObjectListModel; class QStringListModel; class AppState; @@ -18,9 +19,7 @@ public: explicit ObjectListWidget(AppState *appState, QWidget *parent = nullptr); private: - void refresh(); - - QListView *listWidget = nullptr; - AppState *m_appState; - QStringListModel *m_originalModel; + QTreeView *treeWidget = nullptr; + AppState *m_appState = nullptr; + ObjectListModel *m_objectListModel = nullptr; }; diff --git a/apps/mapeditor/src/objectlistmodel.cpp b/apps/mapeditor/src/objectlistmodel.cpp new file mode 100644 index 0000000..0419b45 --- /dev/null +++ b/apps/mapeditor/src/objectlistmodel.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "objectlistmodel.h" +#include "appstate.h" + +#include + +ObjectListModel::ObjectListModel(AppState *appState, QObject *parent) + : QAbstractItemModel(parent) + , m_appState(appState) +{ + m_rootItem = new TreeInformation(); + m_rootItem->type = TreeType::Root; + + connect(m_appState, &AppState::mapLoaded, this, &ObjectListModel::refresh); +} + +int ObjectListModel::rowCount(const QModelIndex &parent) const +{ + TreeInformation *parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + return static_cast(parentItem->children.size()); +} + +int ObjectListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QModelIndex ObjectListModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return {}; + + TreeInformation *parentItem; + + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + TreeInformation *childItem = parentItem->children[row]; + if (childItem) + return createIndex(row, column, childItem); + return {}; +} + +QModelIndex ObjectListModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return {}; + + auto childItem = static_cast(index.internalPointer()); + TreeInformation *parentItem = childItem->parent; + + if (parentItem == m_rootItem) + return {}; + + return createIndex(parentItem->row, 0, parentItem); +} + +QVariant ObjectListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + auto item = static_cast(index.internalPointer()); + + if (item->type == TreeType::File) { + if (role == Qt::DisplayRole) { + return item->name; + } + } else if (item->type == TreeType::Object) { + if (role == Qt::DisplayRole) { + return item->name; + } + } + + return {}; +} + +QVariant ObjectListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + if (section == 0) { + return i18nc("@title:column Object id", "Id"); + } + } + + return QAbstractItemModel::headerData(section, orientation, role); +} + +void ObjectListModel::refresh() +{ + beginResetModel(); + + auto fileItem = new TreeInformation(); + fileItem->type = TreeType::File; + fileItem->parent = m_rootItem; + fileItem->name = QStringLiteral("bg"); + m_rootItem->children.push_back(fileItem); + + for (int i = 0; i < m_appState->bgGroup.num_chunks; i++) { + const auto chunk = m_appState->bgGroup.chunks[i]; + for (int j = 0; j < chunk.num_layers; j++) { + const auto layer = chunk.layers[j]; + for (int z = 0; z < layer.num_objects; z++) { + const auto object = layer.objects[z]; + const QString name = QString::fromLatin1(object.name); + + auto objectItem = new TreeInformation(); + objectItem->type = TreeType::Object; + objectItem->parent = fileItem; + objectItem->name = i18n("Unknown (%1)", object.instance_id); // TODO: do display names if we have them + objectItem->row = z; + fileItem->children.push_back(objectItem); + } + } + } + endResetModel(); +} + +#include "moc_objectlistmodel.cpp" diff --git a/apps/mapeditor/src/objectlistwidget.cpp b/apps/mapeditor/src/objectlistwidget.cpp index 4a12d97..59555df 100644 --- a/apps/mapeditor/src/objectlistwidget.cpp +++ b/apps/mapeditor/src/objectlistwidget.cpp @@ -10,6 +10,7 @@ #include #include "appstate.h" +#include "objectlistmodel.h" ObjectListWidget::ObjectListWidget(AppState *appState, QWidget *parent) : QWidget(parent) @@ -25,7 +26,7 @@ ObjectListWidget::ObjectListWidget(AppState *appState, QWidget *parent) searchModel->setFilterCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); auto searchEdit = new QLineEdit(); - searchEdit->setWhatsThis(i18nc("@info:whatsthis", "Search box for Excel sheet names.")); + searchEdit->setWhatsThis(i18nc("@info:whatsthis", "Search box for objects.")); searchEdit->setPlaceholderText(i18nc("@info:placeholder", "Search…")); searchEdit->setClearButtonEnabled(true); searchEdit->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::BottomEdge})); @@ -34,39 +35,14 @@ ObjectListWidget::ObjectListWidget(AppState *appState, QWidget *parent) }); layout->addWidget(searchEdit); - m_originalModel = new QStringListModel(); - searchModel->setSourceModel(m_originalModel); + m_objectListModel = new ObjectListModel(appState, this); + searchModel->setSourceModel(m_objectListModel); - listWidget = new QListView(); - listWidget->setWhatsThis(i18nc("@info:whatsthis", "A list of Excel sheet names. Select one to view it's contents.")); - listWidget->setModel(searchModel); + treeWidget = new QTreeView(); + treeWidget->setWhatsThis(i18nc("@info:whatsthis", "A list of objects on this map.")); + treeWidget->setModel(searchModel); - layout->addWidget(listWidget); - - connect(m_appState, &AppState::mapLoaded, this, &ObjectListWidget::refresh); -} - -void ObjectListWidget::refresh() -{ - QStringList list; - - for (int i = 0; i < m_appState->bgGroup.num_chunks; i++) { - const auto chunk = m_appState->bgGroup.chunks[i]; - for (int j = 0; j < chunk.num_layers; j++) { - const auto layer = chunk.layers[j]; - for (int z = 0; z < layer.num_objects; z++) { - const auto object = layer.objects[z]; - const QString name = QString::fromLatin1(object.name); - if (true) { // TODO: do display names if we have them - list << i18n("Unknown (%1)", object.instance_id); - } else { - list << name; - } - } - } - } - - m_originalModel->setStringList(list); + layout->addWidget(treeWidget); } #include "moc_objectlistwidget.cpp"