-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add a Qt object tree view in the debug widget.
This shows the main widget and all its children as a tree.
- Loading branch information
Showing
19 changed files
with
346 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later | ||
* Copyright © 2024 The TokTok team. | ||
*/ | ||
|
||
#include "debugobjecttreemodel.h" | ||
|
||
// explicit DebugObjectTreeModel(QObject* parent = nullptr); | ||
// ~DebugObjectTreeModel() override; | ||
|
||
// QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | ||
// QModelIndex parent(const QModelIndex& index) const override; | ||
// 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; | ||
|
||
class DebugObjectTreeModel::TreeItem | ||
{ | ||
public: | ||
const TreeItem* child(int row) | ||
{ | ||
return row >= 0 && row < childCount() ? children.at(row).get() : nullptr; | ||
} | ||
|
||
int childCount() const | ||
{ | ||
return int(children.size()); | ||
} | ||
|
||
int columnCount() const | ||
{ | ||
return 3; | ||
} | ||
|
||
QVariant data(int column) const | ||
{ | ||
switch (column) { | ||
case 0: | ||
return name; | ||
case 1: | ||
return type; | ||
case 2: | ||
return QString::number(address, 16); | ||
default: | ||
return QVariant(); | ||
} | ||
} | ||
|
||
int row() const | ||
{ | ||
if (parent == nullptr) | ||
return 0; | ||
const auto it = std::find_if(parent->children.cbegin(), parent->children.cend(), | ||
[this](const std::unique_ptr<TreeItem>& treeItem) { | ||
return treeItem.get() == this; | ||
}); | ||
|
||
if (it != parent->children.cend()) | ||
return std::distance(parent->children.cbegin(), it); | ||
Q_ASSERT(false); // should not happen | ||
return -1; | ||
} | ||
|
||
TreeItem* parentItem() | ||
{ | ||
return parent; | ||
} | ||
|
||
uintptr_t address = 0; | ||
QString name; | ||
QString type; | ||
TreeItem* parent = nullptr; | ||
std::vector<std::unique_ptr<TreeItem>> children; | ||
}; | ||
|
||
namespace { | ||
std::unique_ptr<DebugObjectTreeModel::TreeItem> buildObjectTree(const QObject* object, | ||
DebugObjectTreeModel::TreeItem* parent) | ||
{ | ||
auto tree = std::make_unique<DebugObjectTreeModel::TreeItem>(); | ||
tree->address = reinterpret_cast<uintptr_t>(object); | ||
tree->name = object->objectName(); | ||
tree->type = QString::fromLatin1(object->metaObject()->className()); | ||
tree->parent = parent; | ||
|
||
for (auto* child : object->children()) { | ||
tree->children.push_back(buildObjectTree(child, tree.get())); | ||
} | ||
|
||
return tree; | ||
} | ||
} // namespace | ||
|
||
DebugObjectTreeModel::DebugObjectTreeModel(QObject* parent) | ||
: QAbstractItemModel(parent) | ||
, root_(std::make_unique<TreeItem>()) | ||
{ | ||
reload(); | ||
} | ||
|
||
DebugObjectTreeModel::~DebugObjectTreeModel() {} | ||
|
||
void DebugObjectTreeModel::reload() | ||
{ | ||
// Find the root object | ||
QObject* root = this; | ||
while (root->parent()) { | ||
root = root->parent(); | ||
} | ||
|
||
auto tree = buildObjectTree(root, root_.get()); | ||
|
||
beginResetModel(); | ||
root_->children.clear(); | ||
root_->children.push_back(std::move(tree)); | ||
endResetModel(); | ||
} | ||
|
||
QModelIndex DebugObjectTreeModel::index(int row, int column, const QModelIndex& parent) const | ||
{ | ||
if (!hasIndex(row, column, parent)) | ||
return {}; | ||
|
||
TreeItem* parentItem = | ||
parent.isValid() ? static_cast<TreeItem*>(parent.internalPointer()) : root_.get(); | ||
|
||
if (auto* childItem = parentItem->child(row)) | ||
return createIndex(row, column, childItem); | ||
return {}; | ||
} | ||
|
||
QModelIndex DebugObjectTreeModel::parent(const QModelIndex& index) const | ||
{ | ||
if (!index.isValid()) | ||
return {}; | ||
|
||
auto* childItem = static_cast<TreeItem*>(index.internalPointer()); | ||
TreeItem* parentItem = childItem->parentItem(); | ||
|
||
return parentItem != root_.get() ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{}; | ||
} | ||
|
||
int DebugObjectTreeModel::rowCount(const QModelIndex& parent) const | ||
{ | ||
if (parent.column() > 0) | ||
return 0; | ||
|
||
const TreeItem* parentItem = | ||
parent.isValid() ? static_cast<const TreeItem*>(parent.internalPointer()) : root_.get(); | ||
|
||
return parentItem->childCount(); | ||
} | ||
|
||
int DebugObjectTreeModel::columnCount(const QModelIndex& parent) const | ||
{ | ||
if (parent.isValid()) | ||
return static_cast<TreeItem*>(parent.internalPointer())->columnCount(); | ||
return root_->columnCount(); | ||
} | ||
|
||
QVariant DebugObjectTreeModel::data(const QModelIndex& index, int role) const | ||
{ | ||
if (!index.isValid() || role != Qt::DisplayRole) | ||
return {}; | ||
|
||
const auto* item = static_cast<const TreeItem*>(index.internalPointer()); | ||
return item->data(index.column()); | ||
} | ||
|
||
Qt::ItemFlags DebugObjectTreeModel::flags(const QModelIndex& index) const | ||
{ | ||
return index.isValid() ? QAbstractItemModel::flags(index) : Qt::ItemFlags(Qt::NoItemFlags); | ||
} | ||
|
||
QVariant DebugObjectTreeModel::headerData(int section, Qt::Orientation orientation, int role) const | ||
{ | ||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { | ||
return {}; | ||
} | ||
|
||
switch (section) { | ||
case 0: | ||
return QStringLiteral("Name"); | ||
case 1: | ||
return QStringLiteral("Type"); | ||
case 2: | ||
return QStringLiteral("Address"); | ||
default: | ||
return {}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* SPDX-License-Identifier: GPL-3.0-or-later | ||
* Copyright © 2024 The TokTok team. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <QAbstractItemModel> | ||
|
||
class DebugObjectTreeModel : public QAbstractItemModel | ||
{ | ||
Q_OBJECT | ||
public: | ||
explicit DebugObjectTreeModel(QObject* parent = nullptr); | ||
~DebugObjectTreeModel() override; | ||
|
||
void reload(); | ||
|
||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; | ||
QModelIndex parent(const QModelIndex& index) const override; | ||
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; | ||
Qt::ItemFlags flags(const QModelIndex& index) const override; | ||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; | ||
|
||
class TreeItem; | ||
|
||
private: | ||
std::unique_ptr<TreeItem> root_; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#include "debugobjecttree.h" | ||
#include "ui_debugobjecttree.h" | ||
|
||
#include "src/model/debug/debugobjecttreemodel.h" | ||
|
||
DebugObjectTree::DebugObjectTree(Style& style, QWidget* parent) | ||
: GenericForm{QPixmap(":/img/settings/general.png"), style, parent} | ||
, ui_(std::make_unique<Ui::DebugObjectTree>()) | ||
, model_(new DebugObjectTreeModel(this)) | ||
{ | ||
ui_->setupUi(this); | ||
|
||
ui_->objectTree->setModel(model_); | ||
ui_->objectTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents); | ||
|
||
connect(ui_->btnReload, &QPushButton::clicked, model_, &DebugObjectTreeModel::reload); | ||
} | ||
|
||
DebugObjectTree::~DebugObjectTree() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#ifndef DEBUGOBJECTTREE_H | ||
#define DEBUGOBJECTTREE_H | ||
|
||
#include "src/widget/form/settings/genericsettings.h" | ||
|
||
#include <memory> | ||
|
||
class DebugObjectTreeModel; | ||
class Style; | ||
|
||
namespace Ui { | ||
class DebugObjectTree; | ||
} | ||
|
||
class DebugObjectTree : public GenericForm | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
explicit DebugObjectTree(Style& style, QWidget* parent = nullptr); | ||
~DebugObjectTree(); | ||
|
||
QString getFormName() final | ||
{ | ||
// Not translated (for now). Debugging only. | ||
return QStringLiteral("Object Tree"); | ||
} | ||
|
||
private: | ||
std::unique_ptr<Ui::DebugObjectTree> ui_; | ||
DebugObjectTreeModel* model_; | ||
}; | ||
|
||
#endif // DEBUGOBJECTTREE_H |
Oops, something went wrong.