Command / property bridges for QML / QtQuick
This commit is contained in:
parent
2eab935dff
commit
e022e4fed5
7 changed files with 673 additions and 0 deletions
|
@ -163,6 +163,12 @@ if (HAVE_QT)
|
|||
QQuickDrawable.hxx
|
||||
QtQuickFGCanvasItem.cxx
|
||||
QtQuickFGCanvasItem.hxx
|
||||
PropertyItemModel.cxx
|
||||
PropertyItemModel.hxx
|
||||
FGQmlInstance.cxx
|
||||
FGQmlInstance.hxx
|
||||
FGQmlPropertyNode.cxx
|
||||
FGQmlPropertyNode.hxx
|
||||
)
|
||||
|
||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||
|
|
106
src/GUI/FGQmlInstance.cxx
Normal file
106
src/GUI/FGQmlInstance.cxx
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "FGQmlInstance.hxx"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJSValueIterator>
|
||||
|
||||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
namespace {
|
||||
|
||||
void convertJSObjectToPropertyNode(QJSValue js, SGPropertyNode *node);
|
||||
|
||||
void convertSingleJSValue(QJSValue v, SGPropertyNode* node)
|
||||
{
|
||||
if (v.isNumber()) {
|
||||
node->setDoubleValue(v.toNumber());
|
||||
} else if (v.isBool()) {
|
||||
node->setBoolValue(v.toBool());
|
||||
} else if (v.isString()) {
|
||||
node->setStringValue(v.toString().toStdString());
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "unhandld JS type";
|
||||
}
|
||||
}
|
||||
|
||||
void convertJSObjectToPropertyNode(QJSValue js, SGPropertyNode* node)
|
||||
{
|
||||
QJSValueIterator it(js);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
const auto propName = it.name().toStdString();
|
||||
QJSValue v = it.value();
|
||||
if (v.isObject()) {
|
||||
// recursion is fun :)
|
||||
SGPropertyNode_ptr childNode = node->getChild(propName, true);
|
||||
convertJSObjectToPropertyNode(v, childNode);
|
||||
} else if (v.isArray()) {
|
||||
// mapping arbitrary JS arrays is slightly uncomfortable, but
|
||||
// this makes the common case work:
|
||||
// foobar: [1,4, "apples", false]
|
||||
// becomes foobar[0] = 1; foobar[1] = 4; foobar[2] = "apples";
|
||||
// foobar[3] = false;
|
||||
// not perfect but useful enough to be worth supporting.
|
||||
QJSValueIterator arrayIt(v);
|
||||
int propIndex = 0;
|
||||
while (arrayIt.hasNext()) {
|
||||
arrayIt.next();
|
||||
SGPropertyNode_ptr childNode = node->getChild(propName, propIndex++, true);
|
||||
if (arrayIt.value().isArray()) {
|
||||
// there is no sensible mapping of this to SGPropertyNode
|
||||
qWarning() << Q_FUNC_INFO <<"arrays of array not possible";
|
||||
} else if (arrayIt.value().isObject()) {
|
||||
convertJSObjectToPropertyNode(arrayIt.value(), childNode);
|
||||
} else {
|
||||
// simple case, just convert the value in place
|
||||
convertSingleJSValue(arrayIt.value(), childNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
convertSingleJSValue(v, node->getChild(propName, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // of anonymous namespace
|
||||
|
||||
FGQmlInstance::FGQmlInstance(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FGQmlInstance::command(QString name, QJSValue args)
|
||||
{
|
||||
const std::string cmdName = name.toStdString();
|
||||
|
||||
SGCommandMgr* mgr = SGCommandMgr::instance();
|
||||
if (!mgr->getCommand(cmdName)) {
|
||||
qWarning() << "No such command" << name;
|
||||
return false;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr propArgs(new SGPropertyNode);
|
||||
|
||||
// convert JSValue args into a property tree.
|
||||
if (args.isObject()) {
|
||||
convertJSObjectToPropertyNode(args, propArgs);
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
return mgr->execute(cmdName, propArgs, globals->get_props());
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *FGQmlInstance::property(QString path, bool create) const
|
||||
{
|
||||
SGPropertyNode_ptr node = fgGetNode(path.toStdString(), create);
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
FGQmlPropertyNode* result = new FGQmlPropertyNode;
|
||||
result->setNode(node);
|
||||
return result;
|
||||
}
|
28
src/GUI/FGQmlInstance.hxx
Normal file
28
src/GUI/FGQmlInstance.hxx
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef FGQMLINSTANCE_HXX
|
||||
#define FGQMLINSTANCE_HXX
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QJSValue>
|
||||
|
||||
// forward decls
|
||||
class FGQmlPropertyNode;
|
||||
|
||||
class FGQmlInstance : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FGQmlInstance(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE bool command(QString name, QJSValue args);
|
||||
|
||||
/**
|
||||
* retrieve a property by its global path
|
||||
*/
|
||||
Q_INVOKABLE FGQmlPropertyNode* property(QString path, bool create = false) const;
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // FGQMLINSTANCE_HXX
|
143
src/GUI/FGQmlPropertyNode.cxx
Normal file
143
src/GUI/FGQmlPropertyNode.cxx
Normal file
|
@ -0,0 +1,143 @@
|
|||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
#include <QDebug>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGQmlPropertyNode::FGQmlPropertyNode(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FGQmlPropertyNode::set(QVariant newValue)
|
||||
{
|
||||
if (!_prop || !_prop->getAttribute(SGPropertyNode::WRITE))
|
||||
return false;
|
||||
|
||||
// we still need to short circuit setting
|
||||
if (newValue == value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_prop->getType()) {
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
_prop->setIntValue(newValue.toInt());
|
||||
case simgear::props::BOOL: _prop->setBoolValue(newValue.toBool());
|
||||
case simgear::props::DOUBLE: _prop->setDoubleValue(newValue.toDouble());
|
||||
case simgear::props::FLOAT: _prop->setFloatValue(newValue.toFloat());
|
||||
case simgear::props::STRING: _prop->setStringValue(newValue.toString().toStdString());
|
||||
|
||||
case simgear::props::VEC3D: {
|
||||
QVector3D v = newValue.value<QVector3D>();
|
||||
_prop->setValue(SGVec3d(v.x(), v.y(), v.z()));
|
||||
}
|
||||
|
||||
case simgear::props::VEC4D: {
|
||||
QVector4D v = newValue.value<QVector4D>();
|
||||
_prop->setValue(SGVec4d(v.x(), v.y(), v.z(), v.w()));
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning() << Q_FUNC_INFO << "handle untyped property writes";
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant FGQmlPropertyNode::value() const
|
||||
{
|
||||
if (!_prop)
|
||||
return {};
|
||||
|
||||
switch (_prop->getType()) {
|
||||
case simgear::props::INT:
|
||||
case simgear::props::LONG:
|
||||
return _prop->getIntValue();
|
||||
case simgear::props::BOOL: return _prop->getBoolValue();
|
||||
case simgear::props::DOUBLE: return _prop->getDoubleValue();
|
||||
case simgear::props::FLOAT: return _prop->getFloatValue();
|
||||
case simgear::props::STRING: return QString::fromStdString(_prop->getStringValue());
|
||||
|
||||
case simgear::props::VEC3D: {
|
||||
const SGVec3d v3 = _prop->getValue<SGVec3d>();
|
||||
return QVariant::fromValue(QVector3D(v3.x(), v3.y(), v3.z()));
|
||||
}
|
||||
|
||||
case simgear::props::VEC4D: {
|
||||
const SGVec4d v4 = _prop->getValue<SGVec4d>();
|
||||
return QVariant::fromValue(QVector4D(v4.x(), v4.y(), v4.z(), v4.w()));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {}; // null qvariant
|
||||
}
|
||||
|
||||
QString FGQmlPropertyNode::path() const
|
||||
{
|
||||
if (!_prop)
|
||||
return QString();
|
||||
|
||||
return QString::fromStdString(_prop->getPath());
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *FGQmlPropertyNode::parentProp() const
|
||||
{
|
||||
if (!_prop || !_prop->getParent())
|
||||
return nullptr;
|
||||
|
||||
auto pp = new FGQmlPropertyNode;
|
||||
pp->setNode(_prop->getParent());
|
||||
return pp;
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::setNode(SGPropertyNode_ptr node)
|
||||
{
|
||||
if (node == _prop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_prop) {
|
||||
_prop->removeChangeListener(this);
|
||||
}
|
||||
|
||||
_prop = node;
|
||||
if (_prop) {
|
||||
_prop->addChangeListener(this, false);
|
||||
}
|
||||
|
||||
emit parentPropChanged(parentProp());
|
||||
emit pathChanged(path());
|
||||
emit valueChangedNotify(value());
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr FGQmlPropertyNode::node() const
|
||||
{
|
||||
return _prop;
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
if (node != _prop) {
|
||||
return;
|
||||
}
|
||||
emit valueChangedNotify(value());
|
||||
}
|
||||
|
||||
void FGQmlPropertyNode::setPath(QString path)
|
||||
{
|
||||
SGPropertyNode_ptr node = fgGetNode(path.toStdString(), false /* don't create */);
|
||||
setNode(node);
|
||||
}
|
||||
|
54
src/GUI/FGQmlPropertyNode.hxx
Normal file
54
src/GUI/FGQmlPropertyNode.hxx
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef FGQMLPROPERTYNODE_HXX
|
||||
#define FGQMLPROPERTYNODE_HXX
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class FGQmlPropertyNode : public QObject,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FGQmlPropertyNode(QObject *parent = nullptr);
|
||||
|
||||
Q_PROPERTY(QVariant value READ value NOTIFY valueChangedNotify)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(FGQmlPropertyNode* parentProp READ parentProp NOTIFY parentPropChanged)
|
||||
|
||||
Q_INVOKABLE bool set(QVariant newValue);
|
||||
|
||||
// children accessor
|
||||
QVariant value() const;
|
||||
|
||||
QString path() const;
|
||||
|
||||
FGQmlPropertyNode* parentProp() const;
|
||||
|
||||
// C++ API, not accessible from QML
|
||||
void setNode(SGPropertyNode_ptr node);
|
||||
|
||||
SGPropertyNode_ptr node() const;
|
||||
protected:
|
||||
// SGPropertyChangeListener API
|
||||
|
||||
void valueChanged(SGPropertyNode * node) override;
|
||||
|
||||
signals:
|
||||
|
||||
void valueChangedNotify(QVariant value);
|
||||
|
||||
void pathChanged(QString path);
|
||||
|
||||
void parentPropChanged(FGQmlPropertyNode* parentProp);
|
||||
|
||||
public slots:
|
||||
|
||||
void setPath(QString path);
|
||||
|
||||
private:
|
||||
SGPropertyNode_ptr _prop;
|
||||
};
|
||||
|
||||
#endif // FGQMLPROPERTYNODE_HXX
|
257
src/GUI/PropertyItemModel.cxx
Normal file
257
src/GUI/PropertyItemModel.cxx
Normal file
|
@ -0,0 +1,257 @@
|
|||
#include "PropertyItemModel.hxx"
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include "FGQmlPropertyNode.hxx"
|
||||
|
||||
class PropertyItemModelRoleMapping : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
|
||||
public:
|
||||
|
||||
QString path() const
|
||||
{
|
||||
return QString::fromStdString(_propertyPath);
|
||||
}
|
||||
|
||||
QString roleName() const
|
||||
{
|
||||
return QString::fromLatin1(_roleName);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setPath(QString path)
|
||||
{
|
||||
if (_propertyPath == path.toStdString());
|
||||
return;
|
||||
|
||||
_propertyPath = path.toStdString();
|
||||
emit pathChanged(path);
|
||||
}
|
||||
|
||||
void setRoleName(QString roleName)
|
||||
{
|
||||
if (_roleName == roleName.toLatin1())
|
||||
return;
|
||||
|
||||
_roleName = roleName.toLatin1();
|
||||
emit roleNameChanged(roleName);
|
||||
}
|
||||
|
||||
signals:
|
||||
void pathChanged(QString path);
|
||||
|
||||
void roleNameChanged(QString roleName);
|
||||
|
||||
private:
|
||||
friend class PropertyItemModel;
|
||||
std::string _propertyPath;
|
||||
QByteArray _roleName;
|
||||
};
|
||||
|
||||
PropertyItemModel::PropertyItemModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int PropertyItemModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return _nodes.size();
|
||||
}
|
||||
|
||||
QVariant PropertyItemModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
if (index.row() >= _nodes.size())
|
||||
return {};
|
||||
|
||||
SGPropertyNode_ptr n = _nodes.at(index.row());
|
||||
Q_ASSERT(n);
|
||||
int mappingIndex = role - Qt::UserRole;
|
||||
if ((mappingIndex < 0) || (mappingIndex >= _roleMappings.size()))
|
||||
return {};
|
||||
|
||||
const std::string& nodePath = _roleMappings.at(mappingIndex)->_propertyPath;
|
||||
SGPropertyNode_ptr valueNode = n->getNode(nodePath);
|
||||
|
||||
// convert property value to a variant
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PropertyItemModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> result;
|
||||
int count = 0;
|
||||
for (auto m : _roleMappings) {
|
||||
result[Qt::UserRole + count++] = m->_roleName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString PropertyItemModel::rootPath() const
|
||||
{
|
||||
return QString::fromStdString(_modelRoot->getPath());
|
||||
}
|
||||
|
||||
QString PropertyItemModel::childNameFilter() const
|
||||
{
|
||||
return _childNameFilter;
|
||||
}
|
||||
|
||||
FGQmlPropertyNode *PropertyItemModel::root() const
|
||||
{
|
||||
auto n = new FGQmlPropertyNode;
|
||||
n->setNode(_modelRoot);
|
||||
return n;
|
||||
}
|
||||
|
||||
void PropertyItemModel::setRootPath(QString rootPath)
|
||||
{
|
||||
if (_modelRoot->getPath() == rootPath.toStdString())
|
||||
return;
|
||||
|
||||
innerSetRoot(fgGetNode(rootPath.toStdString()));
|
||||
}
|
||||
|
||||
void PropertyItemModel::setChildNameFilter(QString childNameFilter)
|
||||
{
|
||||
if (_childNameFilter == childNameFilter)
|
||||
return;
|
||||
|
||||
_childNameFilter = childNameFilter;
|
||||
emit childNameFilterChanged(_childNameFilter);
|
||||
|
||||
beginResetModel();
|
||||
rebuild();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PropertyItemModel::setRoot(FGQmlPropertyNode *root)
|
||||
{
|
||||
if (root->node() == _modelRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
innerSetRoot(root->node());
|
||||
}
|
||||
|
||||
void PropertyItemModel::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
// if node's parent is one of our nodes, emit data changed
|
||||
}
|
||||
|
||||
void PropertyItemModel::childAdded(SGPropertyNode *parent, SGPropertyNode *child)
|
||||
{
|
||||
if (parent == _modelRoot) {
|
||||
if (_childNameFilter.isEmpty() || (child->getNameString() == _childNameFilter.toStdString())) {
|
||||
// insert
|
||||
// need to find appropriate index based on position
|
||||
}
|
||||
}
|
||||
|
||||
// if parent is one of our nodes, emit data changed for it
|
||||
}
|
||||
|
||||
void PropertyItemModel::childRemoved(SGPropertyNode *parent, SGPropertyNode *child)
|
||||
{
|
||||
if (parent == _modelRoot) {
|
||||
// remove row
|
||||
}
|
||||
|
||||
// if parent is one of our nodes, emit data changed for it
|
||||
|
||||
}
|
||||
|
||||
QQmlListProperty<PropertyItemModelRoleMapping> PropertyItemModel::mappings()
|
||||
{
|
||||
return QQmlListProperty<PropertyItemModelRoleMapping>(this, nullptr,
|
||||
&PropertyItemModel::append_mapping,
|
||||
&PropertyItemModel::count_mapping,
|
||||
&PropertyItemModel::at_mapping,
|
||||
&PropertyItemModel::clear_mapping
|
||||
);
|
||||
}
|
||||
|
||||
void PropertyItemModel::rebuild()
|
||||
{
|
||||
if (!_modelRoot) {
|
||||
_nodes.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_childNameFilter.isEmpty()) {
|
||||
// all children
|
||||
_nodes.clear();
|
||||
const int nChildren = _modelRoot->nChildren();
|
||||
_nodes.resize(nChildren);
|
||||
for (int c=0; c < nChildren; ++c) {
|
||||
_nodes[c] = _modelRoot->getChild(c);
|
||||
}
|
||||
} else {
|
||||
_nodes = _modelRoot->getChildren(_childNameFilter.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyItemModel::innerSetRoot(SGPropertyNode_ptr root)
|
||||
{
|
||||
if (_modelRoot) {
|
||||
_modelRoot->removeChangeListener(this);;
|
||||
}
|
||||
beginResetModel();
|
||||
|
||||
_modelRoot = root;
|
||||
if (_modelRoot) {
|
||||
_modelRoot->addChangeListener(this);
|
||||
}
|
||||
|
||||
rebuild();
|
||||
emit rootChanged();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PropertyItemModel::append_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list,
|
||||
PropertyItemModelRoleMapping *mapping)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
model->_roleMappings.append(mapping);
|
||||
model->mappingsChanged();
|
||||
model->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
PropertyItemModelRoleMapping* PropertyItemModel::at_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list, int index)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
return model->_roleMappings.at(index);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PropertyItemModel::count_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
return model->_roleMappings.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PropertyItemModel::clear_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list)
|
||||
{
|
||||
PropertyItemModel *model = qobject_cast<PropertyItemModel *>(list->object);
|
||||
if (model) {
|
||||
model->_roleMappings.clear();
|
||||
model->mappingsChanged();
|
||||
model->rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
#include "PropertyItemModel.moc"
|
79
src/GUI/PropertyItemModel.hxx
Normal file
79
src/GUI/PropertyItemModel.hxx
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef PROPERTYITEMMODEL_HXX
|
||||
#define PROPERTYITEMMODEL_HXX
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlListProperty>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class PropertyItemModelRoleMapping;
|
||||
class FGQmlPropertyNode;
|
||||
|
||||
class PropertyItemModel : public QAbstractListModel,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString rootPath READ rootPath WRITE setRootPath NOTIFY rootChanged)
|
||||
|
||||
Q_PROPERTY(FGQmlPropertyNode* root READ root WRITE setRoot NOTIFY rootChanged)
|
||||
|
||||
Q_PROPERTY(QString childNameFilter READ childNameFilter WRITE setChildNameFilter NOTIFY childNameFilterChanged)
|
||||
Q_PROPERTY(QQmlListProperty<PropertyItemModelRoleMapping> mappings READ mappings NOTIFY mappingsChanged)
|
||||
// list property storing child/role mapping
|
||||
|
||||
QQmlListProperty<PropertyItemModelRoleMapping> mappings();
|
||||
public:
|
||||
PropertyItemModel();
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
QString rootPath() const;
|
||||
|
||||
QString childNameFilter() const;
|
||||
|
||||
FGQmlPropertyNode* root() const;
|
||||
|
||||
public slots:
|
||||
void setRootPath(QString rootPath);
|
||||
|
||||
void setChildNameFilter(QString childNameFilter);
|
||||
|
||||
void setRoot(FGQmlPropertyNode* root);
|
||||
|
||||
signals:
|
||||
void rootChanged();
|
||||
|
||||
void childNameFilterChanged(QString childNameFilter);
|
||||
|
||||
void mappingsChanged();
|
||||
protected:
|
||||
// implement property-change listener to watch for changes to the
|
||||
// underlying nodes
|
||||
void valueChanged(SGPropertyNode *node) override;
|
||||
void childAdded(SGPropertyNode *parent, SGPropertyNode *child) override;
|
||||
void childRemoved(SGPropertyNode *parent, SGPropertyNode *child) override;
|
||||
|
||||
private:
|
||||
void rebuild();
|
||||
|
||||
void innerSetRoot(SGPropertyNode_ptr root);
|
||||
|
||||
static void append_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list,
|
||||
PropertyItemModelRoleMapping *mapping);
|
||||
static PropertyItemModelRoleMapping* at_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list, int index);
|
||||
static int count_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list);
|
||||
static void clear_mapping(QQmlListProperty<PropertyItemModelRoleMapping> *list);
|
||||
|
||||
SGPropertyNode_ptr _modelRoot;
|
||||
QVector<PropertyItemModelRoleMapping*> _roleMappings;
|
||||
QString _childNameFilter;
|
||||
|
||||
mutable std::vector<SGPropertyNode_ptr> _nodes;
|
||||
};
|
||||
|
||||
#endif // PROPERTYITEMMODEL_HXX
|
Loading…
Reference in a new issue