From c1e3fd9f3e433372118b5d3fd68b1a55790e9909 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann <thomas.hartmann@qt.io> Date: Fri, 7 Oct 2016 08:41:57 +0200 Subject: [PATCH] QmlDesigner: Implementing NodeHints This class evaluates expressions from .metainfo files. Those expressions can script and control the behavior of items in the designer. Change-Id: I9f1ec7dd70f1124a684afe6620de5b81c8cc5a30 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io> --- .../designercore/designercore-lib.pri | 2 + .../designercore/include/nodehints.h | 111 ++++++++ .../designercore/metainfo/nodehints.cpp | 258 ++++++++++++++++++ src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 4 files changed, 373 insertions(+) create mode 100644 src/plugins/qmldesigner/designercore/include/nodehints.h create mode 100644 src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri index ab3a4eeebc7..8243afd5529 100644 --- a/src/plugins/qmldesigner/designercore/designercore-lib.pri +++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri @@ -19,6 +19,7 @@ SOURCES += $$PWD/model/abstractview.cpp \ $$PWD/metainfo/metainfo.cpp \ $$PWD/metainfo/metainforeader.cpp \ $$PWD/metainfo/nodemetainfo.cpp \ + $$PWD/metainfo/nodehints.cpp \ $$PWD/metainfo/itemlibraryinfo.cpp \ $$PWD/metainfo/subcomponentmanager.cpp \ $$PWD/model/internalproperty.cpp \ @@ -87,6 +88,7 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \ $$PWD/include/metainfo.h \ $$PWD/include/metainforeader.h \ $$PWD/include/nodemetainfo.h \ + $$PWD/include/nodehints.h \ $$PWD/include/itemlibraryinfo.h \ $$PWD/model/internalproperty.h \ $$PWD/include/modelnode.h \ diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h new file mode 100644 index 00000000000..01222c2e4ae --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QList> +#include <QString> +#include "modelnode.h" + +#include "qmldesignercorelib_global.h" +#include "invalidmetainfoexception.h" + +QT_BEGIN_NAMESPACE +class QDeclarativeContext; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class MetaInfo; +class Model; +class AbstractProperty; + +namespace Internal { + class MetaInfoPrivate; + class MetaInfoReader; + class SubComponentManagerPrivate; + class ItemLibraryEntryData; + class NodeMetaInfoPrivate; +} + +class QMLDESIGNERCORE_EXPORT NodeHints +{ +public: + NodeHints(); + NodeHints(const ModelNode &modelNode); + + bool canBeContainer() const; + bool forceClip() const; + bool doesLayoutChildren() const; + bool canBeDroppedInFormEditor() const; + bool canBeDroppedInNavigator() const; + bool isMovable() const; + bool isStackedContainer() const; + QString indexPropertyForStackedContainer() const; + + QHash<QString, QString> hints() const; + +private: + ModelNode modelNode() const; + bool isValid() const; + Model *model() const; + bool evaluateBooleanExpression(const QString &hintName, bool defaultValue) const; + + ModelNode m_modelNode; + QHash<QString, QString> m_hints; +}; + +namespace Internal { + +class JSObject : public QObject { + + Q_OBJECT + + Q_PROPERTY(bool hasParent READ hasParent NOTIFY modelNodeChanged) + Q_PROPERTY(bool hasChildren READ hasChildren NOTIFY modelNodeChanged) + Q_PROPERTY(bool currentParentIsRoot READ currentParentIsRoot NOTIFY modelNodeChanged) + +public: + JSObject(); + JSObject(QObject *parent = 0); + void setModelNode(const ModelNode &node); + bool hasParent() const; + bool hasChildren() const; + bool currentParentIsRoot() const; + + Q_INVOKABLE bool isSubclassOf(const QString &typeName); + Q_INVOKABLE bool rootItemIsSubclassOf(const QString &typeName); + Q_INVOKABLE bool currentParentIsSubclassOf(const QString &typeName); + +signals: + void modelNodeChanged(); + +private: + ModelNode m_modelNode; +}; + +} //Internal + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp new file mode 100644 index 00000000000..477796516db --- /dev/null +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "nodehints.h" +#include "model.h" + +#include "metainfo.h" +#include <enumeration.h> +#include <rewriterview.h> +#include <propertyparser.h> +#include <nodeabstractproperty.h> + +#include <QDebug> + +#include <qmljs/qmljsscopechain.h> +#include <qmljs/parser/qmljsast_p.h> +#include <qmljs/qmljsmodelmanagerinterface.h> +#include <qmljs/qmljsvalueowner.h> +#include <languageutils/fakemetaobject.h> + +#include <utils/qtcassert.h> + +#include <itemlibraryinfo.h> + +#include <QJSEngine> + +namespace QmlDesigner { + + +namespace Internal { + +static QJSEngine *s_qJSEngine= nullptr; +static JSObject *s_jsObject = nullptr; + +static QVariant evaluateExpression(const QString &expression, const ModelNode &modelNode) +{ + if (!s_qJSEngine) { + s_qJSEngine = new QJSEngine; + s_jsObject = new JSObject(s_qJSEngine); + QJSValue jsValue = s_qJSEngine->newQObject(s_jsObject); + s_qJSEngine->globalObject().setProperty("model", jsValue); + } + + s_jsObject->setModelNode(modelNode); + return s_qJSEngine->evaluate(expression).toVariant(); +} + +} //Internal + +QmlDesigner::NodeHints::NodeHints() +{ + +} + +QmlDesigner::NodeHints::NodeHints(const ModelNode &node) : m_modelNode(node) +{ + if (isValid()) { + const ItemLibraryInfo *libraryInfo = model()->metaInfo().itemLibraryInfo(); + QList <ItemLibraryEntry> itemLibraryEntryList = libraryInfo->entriesForType( + modelNode().type(), modelNode().majorVersion(), modelNode().minorVersion()); + m_hints = itemLibraryEntryList.first().hints(); + } +} + +bool NodeHints::canBeContainer() const +{ + /* The default is true for now to avoid confusion. Once our .metaInfo files in Qt + use the feature we can change the default to false. */ + + if (!isValid()) + return true; + + return evaluateBooleanExpression("canBeContainer", true); +} + +bool NodeHints::forceClip() const +{ + if (!isValid()) + return false; + + return evaluateBooleanExpression("forceClip", false); +} + +bool NodeHints::doesLayoutChildren() const +{ + if (!isValid()) + return false; + + return evaluateBooleanExpression("doesLayoutChildren", false); +} + +bool NodeHints::canBeDroppedInFormEditor() const +{ + if (!isValid()) + return false; + + return evaluateBooleanExpression("canBeDroppedInFormEditor", false); +} + +bool NodeHints::canBeDroppedInNavigator() const +{ + if (!isValid()) + return false; + + return evaluateBooleanExpression("canBeDroppedInNavigator", false); +} + +bool NodeHints::isMovable() const +{ + if (!isValid()) + return true; + + return evaluateBooleanExpression("isMovable", true); +} + +bool NodeHints::isStackedContainer() const +{ + if (!isValid()) + return false; + + return evaluateBooleanExpression("isStackedContainer", false); +} + +QString NodeHints::indexPropertyForStackedContainer() const +{ + if (!isValid()) + return QString(); + + const QString expression = m_hints.value("indexPropertyForStackedContainer"); + + if (expression.isEmpty()) + return QString(); + + return Internal::evaluateExpression(expression, modelNode()).toString(); +} + +QHash<QString, QString> NodeHints::hints() const +{ + return m_hints; +} + +ModelNode NodeHints::modelNode() const +{ + return m_modelNode; +} + +bool NodeHints::isValid() const +{ + return modelNode().isValid(); +} + +Model *NodeHints::model() const +{ + return modelNode().model(); +} + +bool NodeHints::evaluateBooleanExpression(const QString &hintName, bool defaultValue) const +{ + const QString expression = m_hints.value(hintName); + + if (expression.isEmpty()) + return defaultValue; + + return Internal::evaluateExpression(expression, modelNode()).toBool(); +} + +namespace Internal { + +QmlDesigner::Internal::JSObject::JSObject::JSObject(QObject *parent) : QObject(parent) +{ + +} + +void JSObject::setModelNode(const ModelNode &node) +{ + m_modelNode = node; + emit modelNodeChanged(); +} + +bool JSObject::hasParent() const +{ + return !m_modelNode.isRootNode() + && m_modelNode.hasParentProperty(); +} + +bool JSObject::hasChildren() const +{ + return m_modelNode.hasAnySubModelNodes(); +} + +bool JSObject::currentParentIsRoot() const +{ + return m_modelNode.hasParentProperty() + && m_modelNode.parentProperty().isValid() + && m_modelNode.parentProperty().parentModelNode().isRootNode(); +} + +bool JSObject::isSubclassOf(const QString &typeName) +{ + NodeMetaInfo metaInfo = m_modelNode.metaInfo(); + + if (metaInfo.isValid()) + metaInfo.isSubclassOf(typeName.toUtf8()); + + return false; +} + +bool JSObject::rootItemIsSubclassOf(const QString &typeName) +{ + NodeMetaInfo metaInfo = m_modelNode.view()->rootModelNode().metaInfo(); + + if (metaInfo.isValid()) + metaInfo.isSubclassOf(typeName.toUtf8()); + + return false; +} + +bool JSObject::currentParentIsSubclassOf(const QString &typeName) +{ + if ( m_modelNode.hasParentProperty() + && m_modelNode.parentProperty().isValid()) { + NodeMetaInfo metaInfo = m_modelNode.parentProperty().parentModelNode().metaInfo(); + if (metaInfo.isValid()) + metaInfo.isSubclassOf(typeName.toUtf8()); + } + return false; +} + +JSObject::JSObject() +{ + +} + +} //Internal + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index b9954200c11..8e24fd09a7f 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -257,6 +257,7 @@ Project { "include/nodeinstanceview.h", "include/nodelistproperty.h", "include/nodemetainfo.h", + "include/nodehints.h", "include/nodeproperty.h", "include/notimplementedexception.h", "include/plaintexteditmodifier.h", @@ -294,6 +295,7 @@ Project { "metainfo/metainfo.cpp", "metainfo/metainforeader.cpp", "metainfo/nodemetainfo.cpp", + "metainfo/nodehints.cpp", "metainfo/subcomponentmanager.cpp", "model/abstractproperty.cpp", "model/abstractview.cpp", -- GitLab