tools for qml item properties
This commit is contained in:
@@ -31,6 +31,7 @@ static const char *kQuickWindowModel = "com.kdab.GammaRay.QuickWindowModel";
|
||||
static const char *kQuickItemModel = "com.kdab.GammaRay.QuickItemModel";
|
||||
static const char *kQuickSceneGraphModel = "com.kdab.GammaRay.QuickSceneGraphModel";
|
||||
static const char *kBaseName = "com.kdab.GammaRay.QuickSceneGraph";
|
||||
static const char *kItemBaseName = "com.kdab.GammaRay.QuickItem";
|
||||
static const char *kMaterialIface = "com.kdab.GammaRay.MaterialExtensionInterface";
|
||||
static const char *kQuickInspectorIface = "com.kdab.GammaRay.QuickInspectorInterface/1.0";
|
||||
|
||||
@@ -732,7 +733,159 @@ QString SceneGraphTools::getMaterialProperties(const QString &address) const
|
||||
return QString::fromUtf8(QJsonDocument(out).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
// --- Rendering visualization ---
|
||||
// --- QML Item selection + properties ---
|
||||
|
||||
QString SceneGraphTools::ensureItemSelected(const QString &address) const
|
||||
{
|
||||
if (const QString e = ensureSession(); !e.isEmpty())
|
||||
return e;
|
||||
|
||||
if (address.isEmpty())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("address is required"))).toJson(QJsonDocument::Compact));
|
||||
|
||||
// If already selected, nothing to do.
|
||||
if (address == m_selectedItemAddress)
|
||||
return {};
|
||||
|
||||
auto *itemModel = m_session->model(QString::fromUtf8(kQuickItemModel));
|
||||
if (!itemModel)
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("QuickItemModel not available"))).toJson(QJsonDocument::Compact));
|
||||
|
||||
const QModelIndex idx = findNodeByAddress(itemModel, address);
|
||||
if (!idx.isValid())
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("item %1 not found in QML item tree (still loading?)").arg(address)))
|
||||
.toJson(QJsonDocument::Compact));
|
||||
|
||||
// Create the selection model client BEFORE selecting.
|
||||
auto *selModel = m_session->selectionModel(itemModel);
|
||||
|
||||
// CRITICAL: Create the item properties RemoteModel BEFORE sending the selection,
|
||||
// so the server is already monitoring when the property model resets/populates.
|
||||
auto *propModel = m_session->model(QStringLiteral("%1.properties").arg(QString::fromUtf8(kItemBaseName)));
|
||||
|
||||
// Wait for the SelectionModelClient's 125ms default-selection timer to fire
|
||||
// and settle before our override.
|
||||
QEventLoop loop;
|
||||
settle(loop, 300);
|
||||
|
||||
// Select the item via client-side selection model.
|
||||
if (selModel && idx.isValid())
|
||||
selModel->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows | QItemSelectionModel::Current);
|
||||
|
||||
// Wait for the selection to be processed and property data to arrive.
|
||||
settle(loop, 2000);
|
||||
|
||||
m_selectedItemAddress = address;
|
||||
|
||||
// Prime the property model to fetch its row count and cell data.
|
||||
if (propModel) { propModel->rowCount(); primeAndWait(propModel, 4, 300, 500, 1500); }
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QString SceneGraphTools::selectQuickItem(const QString &address) const
|
||||
{
|
||||
m_selectedItemAddress.clear(); // force reselect
|
||||
const QString e = ensureItemSelected(address);
|
||||
if (!e.isEmpty())
|
||||
return e;
|
||||
|
||||
// Report what we know about the selected item.
|
||||
auto *itemModel = m_session->model(QString::fromUtf8(kQuickItemModel));
|
||||
const QModelIndex idx = findNodeByAddress(itemModel, address);
|
||||
QString type;
|
||||
int flags = 0;
|
||||
int propertyCount = 0;
|
||||
if (idx.isValid()) {
|
||||
const QModelIndex typeIdx = itemModel->index(idx.row(), 1, idx.parent());
|
||||
type = itemModel->data(typeIdx, Qt::DisplayRole).toString();
|
||||
flags = itemModel->data(idx, kItemFlagsRole).toInt();
|
||||
}
|
||||
|
||||
auto *propModel = m_session->model(QStringLiteral("%1.properties").arg(QString::fromUtf8(kItemBaseName)));
|
||||
if (propModel)
|
||||
propertyCount = propModel->rowCount();
|
||||
|
||||
QJsonObject out = {
|
||||
{ QStringLiteral("address"), address },
|
||||
{ QStringLiteral("type"), type },
|
||||
{ QStringLiteral("flags"), flagsToJson(flags) },
|
||||
{ QStringLiteral("propertyCount"), propertyCount },
|
||||
};
|
||||
return QString::fromUtf8(QJsonDocument(out).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
QString SceneGraphTools::getItemProperties(const QString &address) const
|
||||
{
|
||||
const QString e = ensureItemSelected(address);
|
||||
if (!e.isEmpty())
|
||||
return e;
|
||||
|
||||
auto *m = m_session->model(QStringLiteral("%1.properties").arg(QString::fromUtf8(kItemBaseName)));
|
||||
if (!m)
|
||||
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)
|
||||
return QString::fromUtf8(QJsonDocument(errorJson(
|
||||
QStringLiteral("no properties available (did you select a valid QML item?)")))
|
||||
.toJson(QJsonDocument::Compact));
|
||||
|
||||
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());
|
||||
|
||||
// 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 < rows; ++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();
|
||||
|
||||
// Check if this property has children (nested sub-properties).
|
||||
QJsonObject prop;
|
||||
prop.insert(QStringLiteral("value"), val);
|
||||
prop.insert(QStringLiteral("type"), ptype);
|
||||
|
||||
if (m->hasChildren(m->index(r, 0))) {
|
||||
// For grouped properties (anchors, transform, etc.), collect children.
|
||||
QJsonObject children;
|
||||
for (int cr = 0; cr < m->rowCount(m->index(r, 0)); ++cr) {
|
||||
const QString cname = m->data(m->index(cr, 0, m->index(r, 0)), Qt::DisplayRole).toString();
|
||||
const QString cval = m->data(m->index(cr, 1, m->index(r, 0)), Qt::DisplayRole).toString();
|
||||
const QString ctype = cols > 2 ? m->data(m->index(cr, 2, m->index(r, 0)), Qt::DisplayRole).toString() : QString();
|
||||
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));
|
||||
}
|
||||
|
||||
QString SceneGraphTools::setRenderMode(const QString &mode) const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user