basic qt widgets support
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "scenegraph_tools.h"
|
||||
#include "gammaray_session.h"
|
||||
#include "material_interface.h"
|
||||
#include "property_reader.h"
|
||||
#include "quickinspector_types.h"
|
||||
|
||||
#include <endpoint.h>
|
||||
@@ -40,6 +41,19 @@ static constexpr int kIsCoordinateRole = 257;
|
||||
static constexpr int kDrawingModeRole = 257;
|
||||
static constexpr int kRenderRole = 258;
|
||||
|
||||
// Returns a non-empty error string if the QuickInspector is not active.
|
||||
// Guards QML-specific tools against widget-only target processes.
|
||||
static QString ensureQuickInspector()
|
||||
{
|
||||
auto *ep = GammaRay::Endpoint::instance();
|
||||
if (!ep)
|
||||
return QStringLiteral("no GammaRay endpoint");
|
||||
if (ep->objectAddress(QString::fromUtf8(kQuickInspectorIface))
|
||||
== GammaRay::Protocol::InvalidObjectAddress)
|
||||
return QStringLiteral("QuickInspector not active — target app has no QQuickWindow");
|
||||
return {};
|
||||
}
|
||||
|
||||
// --- async helpers (from earlier scaffold) ---
|
||||
|
||||
static void waitForData(const QAbstractItemModel *m, int column, int timeoutMs)
|
||||
@@ -324,6 +338,8 @@ QString SceneGraphTools::listQuickWindows() const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *m = m_session->model(QString::fromUtf8(kQuickWindowModel));
|
||||
if (!m)
|
||||
@@ -348,6 +364,8 @@ QString SceneGraphTools::selectQuickWindow(int index) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *ep = GammaRay::Endpoint::instance();
|
||||
if (!ep)
|
||||
@@ -367,6 +385,8 @@ QString SceneGraphTools::listQuickItems() const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *m = m_session->model(QString::fromUtf8(kQuickItemModel));
|
||||
if (!m)
|
||||
@@ -384,6 +404,8 @@ QString SceneGraphTools::listScenegraphNodes() const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *m = m_session->model(QString::fromUtf8(kQuickSceneGraphModel));
|
||||
if (!m)
|
||||
@@ -444,6 +466,8 @@ QString SceneGraphTools::ensureNodeSelected(const QString &address) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return e;
|
||||
|
||||
if (address.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
@@ -739,6 +763,8 @@ QString SceneGraphTools::ensureItemSelected(const QString &address) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return e;
|
||||
|
||||
if (address.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
@@ -840,127 +866,12 @@ QString SceneGraphTools::getItemProperties(const QString &address) const
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("item property model not available"))).toJson(QJsonDocument::Compact));
|
||||
|
||||
// Item property properties model has 4 columns:
|
||||
// 0 = property name, 1 = value (display string), 2 = type name, 3 = class name
|
||||
waitForRows(m, 3000);
|
||||
|
||||
const int rows = m->rowCount();
|
||||
if (rows == 0)
|
||||
QJsonObject out = readAggregatedPropertyModel(m);
|
||||
if (out.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("no properties available (did you select a valid QML item?)")))
|
||||
.toJson(QJsonDocument::Compact));
|
||||
|
||||
// The RemoteModel uses lazy-fetch: data() triggers a fetch and returns
|
||||
// "Loading..." until the fetch response arrives on the event loop.
|
||||
// Poll until column 0 data arrives for at least one row.
|
||||
{
|
||||
QEventLoop loop;
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
||||
bool gotData = false;
|
||||
while (std::chrono::steady_clock::now() < deadline && !gotData) {
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
const QString val = m->data(m->index(r, 0), Qt::DisplayRole).toString();
|
||||
if (!val.isEmpty() && val != QStringLiteral("Loading...")) {
|
||||
gotData = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gotData) {
|
||||
QTimer::singleShot(200, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-read rowCount after polling (more rows may have arrived)
|
||||
const int actualRows = m->rowCount();
|
||||
const int cols = m->columnCount();
|
||||
|
||||
QJsonObject out;
|
||||
QStringList headers;
|
||||
for (int c = 0; c < cols; ++c)
|
||||
headers.append(m->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString());
|
||||
|
||||
// Pre-fetch children for grouped properties. RemoteModel uses lazy-fetch:
|
||||
// rowCount(parent) triggers an async request and returns 0 until the reply
|
||||
// arrives on the event loop. After polling, hasChildren() works correctly.
|
||||
for (int r = 0; r < actualRows; ++r) {
|
||||
m->rowCount(m->index(r, 0));
|
||||
}
|
||||
// Poll to let row count responses arrive
|
||||
if (actualRows > 0) {
|
||||
QEventLoop l2;
|
||||
QTimer::singleShot(500, &l2, &QEventLoop::quit);
|
||||
l2.exec();
|
||||
}
|
||||
// Second pass: ensure child data is fetched for rows that have children
|
||||
for (int r = 0; r < actualRows; ++r) {
|
||||
const QModelIndex idx0 = m->index(r, 0);
|
||||
if (m->hasChildren(idx0) && m->canFetchMore(idx0))
|
||||
m->fetchMore(idx0);
|
||||
}
|
||||
if (actualRows > 0) {
|
||||
QEventLoop l2b;
|
||||
QTimer::singleShot(500, &l2b, &QEventLoop::quit);
|
||||
l2b.exec();
|
||||
}
|
||||
|
||||
// Top-level properties (simple Q_PROPERTY values like x, y, width, height...)
|
||||
QJsonObject simpleProps;
|
||||
// Nested property groups (anchors, transform, etc.) will have children.
|
||||
QJsonObject groups;
|
||||
for (int r = 0; r < actualRows; ++r) {
|
||||
const QString name = m->data(m->index(r, 0), Qt::DisplayRole).toString();
|
||||
const QString val = m->data(m->index(r, 1), Qt::DisplayRole).toString();
|
||||
const QString ptype = cols > 2 ? m->data(m->index(r, 2), Qt::DisplayRole).toString() : QString();
|
||||
|
||||
QJsonObject prop;
|
||||
prop.insert(QStringLiteral("value"), val);
|
||||
prop.insert(QStringLiteral("type"), ptype);
|
||||
|
||||
const QModelIndex idx0 = m->index(r, 0);
|
||||
if (m->hasChildren(idx0)) {
|
||||
// For grouped properties (anchors, transform, etc.), collect children.
|
||||
// Children also use lazy-fetch, so poll if needed.
|
||||
const int childRows = m->rowCount(idx0);
|
||||
if (childRows > 0) {
|
||||
// Trigger child data fetches first
|
||||
for (int cr = 0; cr < childRows; ++cr) {
|
||||
m->data(m->index(cr, 0, idx0), Qt::DisplayRole);
|
||||
m->data(m->index(cr, 1, idx0), Qt::DisplayRole);
|
||||
}
|
||||
// Poll to let child data arrive
|
||||
{
|
||||
QEventLoop l3;
|
||||
QTimer::singleShot(500, &l3, &QEventLoop::quit);
|
||||
l3.exec();
|
||||
}
|
||||
// Read actual child data
|
||||
QJsonObject children;
|
||||
for (int cr = 0; cr < childRows; ++cr) {
|
||||
const QString cname = m->data(m->index(cr, 0, idx0), Qt::DisplayRole).toString();
|
||||
const QString cval = m->data(m->index(cr, 1, idx0), Qt::DisplayRole).toString();
|
||||
const QString ctype = cols > 2 ? m->data(m->index(cr, 2, idx0), Qt::DisplayRole).toString() : QString();
|
||||
if (cname.isEmpty() || cname == QStringLiteral("Loading..."))
|
||||
continue;
|
||||
QJsonObject child;
|
||||
child.insert(QStringLiteral("value"), cval);
|
||||
child.insert(QStringLiteral("type"), ctype);
|
||||
children.insert(cname, child);
|
||||
}
|
||||
if (!children.isEmpty())
|
||||
prop.insert(QStringLiteral("children"), children);
|
||||
}
|
||||
groups.insert(name, prop);
|
||||
} else {
|
||||
simpleProps.insert(name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
out.insert(QStringLiteral("properties"), simpleProps);
|
||||
if (!groups.isEmpty())
|
||||
out.insert(QStringLiteral("groups"), groups);
|
||||
|
||||
return QString::fromUtf8(QJsonDocument(out).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
@@ -968,6 +879,8 @@ QString SceneGraphTools::setRenderMode(const QString &mode) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *ep = GammaRay::Endpoint::instance();
|
||||
if (!ep)
|
||||
@@ -1009,6 +922,8 @@ QString SceneGraphTools::setSlowMode(bool enabled) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
if (const QString e = ensureQuickInspector(); !e.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(e)).toJson(QJsonDocument::Compact));
|
||||
|
||||
auto *ep = GammaRay::Endpoint::instance();
|
||||
if (!ep)
|
||||
|
||||
Reference in New Issue
Block a user