forked from qt-creator/qt-creator
QmlDesigner: Improve connection editor dialog
* Add ComboBoxes to connection editor dialog * Add type toggle to dialog * Add slots and properties to ComboBoxes * Parse connection expression and fill ComboBoxes Task-number: QDS-2498 Task-number: QDS-2495 Task-number: QDS-2496 Change-Id: I2cca6d4c85d1508e54d4ad8863056f22ad777ae6 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Henning Gründl
parent
a489fc18eb
commit
513d1e7e02
@@ -586,8 +586,11 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
SOURCES_PREFIX components/bindingeditor
|
SOURCES_PREFIX components/bindingeditor
|
||||||
SOURCES bindingeditor.cpp bindingeditor.h
|
SOURCES bindingeditor.cpp bindingeditor.h
|
||||||
actioneditor.cpp actioneditor.h
|
actioneditor.cpp actioneditor.h
|
||||||
|
abstracteditordialog.cpp abstracteditordialog.h
|
||||||
|
actioneditordialog.cpp actioneditordialog.h
|
||||||
bindingeditordialog.cpp bindingeditordialog.h
|
bindingeditordialog.cpp bindingeditordialog.h
|
||||||
bindingeditorwidget.cpp bindingeditorwidget.h
|
bindingeditorwidget.cpp bindingeditorwidget.h
|
||||||
|
connectionvisitor.cpp connectionvisitor.h
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
@@ -0,0 +1,183 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "abstracteditordialog.h"
|
||||||
|
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <qmldesigner/qmldesignerplugin.h>
|
||||||
|
#include <qmljseditor/qmljseditor.h>
|
||||||
|
#include <qmljseditor/qmljseditordocument.h>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
AbstractEditorDialog::AbstractEditorDialog(QWidget *parent, const QString &title)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_titleString(title)
|
||||||
|
{
|
||||||
|
setWindowFlag(Qt::Tool, true);
|
||||||
|
setWindowTitle(defaultTitle());
|
||||||
|
setModal(false);
|
||||||
|
|
||||||
|
setupJSEditor();
|
||||||
|
setupUIComponents();
|
||||||
|
|
||||||
|
QObject::connect(m_buttonBox, &QDialogButtonBox::accepted,
|
||||||
|
this, &AbstractEditorDialog::accepted);
|
||||||
|
QObject::connect(m_buttonBox, &QDialogButtonBox::rejected,
|
||||||
|
this, &AbstractEditorDialog::rejected);
|
||||||
|
QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked,
|
||||||
|
this, &AbstractEditorDialog::accepted);
|
||||||
|
QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged,
|
||||||
|
this, &AbstractEditorDialog::textChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractEditorDialog::~AbstractEditorDialog()
|
||||||
|
{
|
||||||
|
delete m_editor; // m_editorWidget is handled by basetexteditor destructor
|
||||||
|
delete m_buttonBox;
|
||||||
|
delete m_comboBoxLayout;
|
||||||
|
delete m_verticalLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::showWidget()
|
||||||
|
{
|
||||||
|
this->show();
|
||||||
|
this->raise();
|
||||||
|
m_editorWidget->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::showWidget(int x, int y)
|
||||||
|
{
|
||||||
|
showWidget();
|
||||||
|
move(QPoint(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AbstractEditorDialog::editorValue() const
|
||||||
|
{
|
||||||
|
if (!m_editorWidget)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_editorWidget->document()->toPlainText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::setEditorValue(const QString &text)
|
||||||
|
{
|
||||||
|
if (m_editorWidget)
|
||||||
|
m_editorWidget->document()->setPlainText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::unregisterAutoCompletion()
|
||||||
|
{
|
||||||
|
if (m_editorWidget)
|
||||||
|
m_editorWidget->unregisterAutoCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AbstractEditorDialog::defaultTitle() const
|
||||||
|
{
|
||||||
|
return m_titleString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::setupJSEditor()
|
||||||
|
{
|
||||||
|
static BindingEditorFactory f;
|
||||||
|
m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor());
|
||||||
|
m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget());
|
||||||
|
|
||||||
|
Core::Context context = m_editor->context();
|
||||||
|
context.prepend(BINDINGEDITOR_CONTEXT_ID);
|
||||||
|
m_editorWidget->m_context->setContext(context);
|
||||||
|
|
||||||
|
auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor();
|
||||||
|
|
||||||
|
m_editorWidget->qmljsdocument = qobject_cast<QmlJSEditor::QmlJSEditorWidget *>(
|
||||||
|
qmlDesignerEditor->widget())->qmlJsEditorDocument();
|
||||||
|
|
||||||
|
m_editorWidget->setLineNumbersVisible(false);
|
||||||
|
m_editorWidget->setMarksVisible(false);
|
||||||
|
m_editorWidget->setCodeFoldingSupported(false);
|
||||||
|
m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
m_editorWidget->setTabChangesFocus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::setupUIComponents()
|
||||||
|
{
|
||||||
|
m_verticalLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
m_comboBoxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
m_editorWidget->setParent(this);
|
||||||
|
m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
||||||
|
m_editorWidget->show();
|
||||||
|
|
||||||
|
m_buttonBox = new QDialogButtonBox(this);
|
||||||
|
m_buttonBox->setOrientation(Qt::Horizontal);
|
||||||
|
m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
|
||||||
|
|
||||||
|
m_verticalLayout->addLayout(m_comboBoxLayout);
|
||||||
|
m_verticalLayout->addWidget(m_editorWidget);
|
||||||
|
m_verticalLayout->addWidget(m_buttonBox);
|
||||||
|
|
||||||
|
this->resize(660, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractEditorDialog::isNumeric(const TypeName &type)
|
||||||
|
{
|
||||||
|
static QList<TypeName> numericTypes = {"double", "int", "real"};
|
||||||
|
return numericTypes.contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractEditorDialog::isColor(const TypeName &type)
|
||||||
|
{
|
||||||
|
static QList<TypeName> colorTypes = {"QColor", "color"};
|
||||||
|
return colorTypes.contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractEditorDialog::isVariant(const TypeName &type)
|
||||||
|
{
|
||||||
|
static QList<TypeName> variantTypes = {"alias", "unknown", "variant", "var"};
|
||||||
|
return variantTypes.contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractEditorDialog::textChanged()
|
||||||
|
{
|
||||||
|
if (m_lock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_lock = true;
|
||||||
|
adjustProperties();
|
||||||
|
m_lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QmlDesigner namespace
|
@@ -0,0 +1,93 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ABSTRACTEDITORDIALOG_H
|
||||||
|
#define ABSTRACTEDITORDIALOG_H
|
||||||
|
|
||||||
|
#include <bindingeditor/bindingeditorwidget.h>
|
||||||
|
#include <qmldesignercorelib_global.h>
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QDialogButtonBox;
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QHBoxLayout;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class AbstractEditorDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AbstractEditorDialog(QWidget *parent = nullptr, const QString &title = tr("Untitled Editor"));
|
||||||
|
~AbstractEditorDialog() override;
|
||||||
|
|
||||||
|
void showWidget();
|
||||||
|
void showWidget(int x, int y);
|
||||||
|
|
||||||
|
QString editorValue() const;
|
||||||
|
void setEditorValue(const QString &text);
|
||||||
|
|
||||||
|
virtual void adjustProperties()= 0;
|
||||||
|
|
||||||
|
void unregisterAutoCompletion();
|
||||||
|
|
||||||
|
QString defaultTitle() const;
|
||||||
|
|
||||||
|
BindingEditorWidget *bindingEditorWidget() const
|
||||||
|
{
|
||||||
|
return m_editorWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setupJSEditor();
|
||||||
|
void setupUIComponents();
|
||||||
|
|
||||||
|
static bool isNumeric(const TypeName &type);
|
||||||
|
static bool isColor(const TypeName &type);
|
||||||
|
static bool isVariant(const TypeName &type);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void textChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TextEditor::BaseTextEditor *m_editor = nullptr;
|
||||||
|
BindingEditorWidget *m_editorWidget = nullptr;
|
||||||
|
QVBoxLayout *m_verticalLayout = nullptr;
|
||||||
|
QDialogButtonBox *m_buttonBox = nullptr;
|
||||||
|
QHBoxLayout *m_comboBoxLayout = nullptr;
|
||||||
|
bool m_lock = false;
|
||||||
|
QString m_titleString;
|
||||||
|
|
||||||
|
const QString undefinedString = {"[Undefined]"};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ABSTRACTEDITORDIALOG_H
|
@@ -28,6 +28,7 @@
|
|||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
|
#include <bindingeditor/actioneditordialog.h>
|
||||||
|
|
||||||
#include <metainfo.h>
|
#include <metainfo.h>
|
||||||
#include <qmlmodelnodeproxy.h>
|
#include <qmlmodelnodeproxy.h>
|
||||||
@@ -35,6 +36,14 @@
|
|||||||
#include <nodelistproperty.h>
|
#include <nodelistproperty.h>
|
||||||
#include <propertyeditorvalue.h>
|
#include <propertyeditorvalue.h>
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
|
#include <qmljs/qmljsscopechain.h>
|
||||||
|
#include <qmljs/qmljsvalueowner.h>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(ceLog, "qtc.qmldesigner.connectioneditor", QtWarningMsg)
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
static ActionEditor *s_lastActionEditor = nullptr;
|
static ActionEditor *s_lastActionEditor = nullptr;
|
||||||
@@ -58,15 +67,14 @@ void ActionEditor::prepareDialog()
|
|||||||
{
|
{
|
||||||
if (s_lastActionEditor)
|
if (s_lastActionEditor)
|
||||||
s_lastActionEditor->hideWidget();
|
s_lastActionEditor->hideWidget();
|
||||||
|
|
||||||
s_lastActionEditor = this;
|
s_lastActionEditor = this;
|
||||||
|
|
||||||
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent(),
|
m_dialog = new ActionEditorDialog(Core::ICore::dialogParent());
|
||||||
BindingEditorDialog::DialogType::ActionDialog);
|
|
||||||
|
|
||||||
|
QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
|
||||||
QObject::connect(m_dialog, &BindingEditorDialog::accepted,
|
|
||||||
this, &ActionEditor::accepted);
|
this, &ActionEditor::accepted);
|
||||||
QObject::connect(m_dialog, &BindingEditorDialog::rejected,
|
QObject::connect(m_dialog, &AbstractEditorDialog::rejected,
|
||||||
this, &ActionEditor::rejected);
|
this, &ActionEditor::rejected);
|
||||||
|
|
||||||
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
@@ -88,14 +96,14 @@ void ActionEditor::hideWidget()
|
|||||||
{
|
{
|
||||||
if (s_lastActionEditor == this)
|
if (s_lastActionEditor == this)
|
||||||
s_lastActionEditor = nullptr;
|
s_lastActionEditor = nullptr;
|
||||||
if (m_dialog)
|
|
||||||
{
|
if (m_dialog) {
|
||||||
m_dialog->unregisterAutoCompletion(); // we have to do it separately, otherwise we have an autocompletion action override
|
m_dialog->unregisterAutoCompletion(); // we have to do it separately, otherwise we have an autocompletion action override
|
||||||
m_dialog->close();
|
m_dialog->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ActionEditor::bindingValue() const
|
QString ActionEditor::connectionValue() const
|
||||||
{
|
{
|
||||||
if (!m_dialog)
|
if (!m_dialog)
|
||||||
return {};
|
return {};
|
||||||
@@ -103,7 +111,7 @@ QString ActionEditor::bindingValue() const
|
|||||||
return m_dialog->editorValue();
|
return m_dialog->editorValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionEditor::setBindingValue(const QString &text)
|
void ActionEditor::setConnectionValue(const QString &text)
|
||||||
{
|
{
|
||||||
if (m_dialog)
|
if (m_dialog)
|
||||||
m_dialog->setEditorValue(text);
|
m_dialog->setEditorValue(text);
|
||||||
@@ -129,11 +137,160 @@ void ActionEditor::setModelIndex(const QModelIndex &index)
|
|||||||
m_index = index;
|
m_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActionEditor::setModelNode(const ModelNode &modelNode)
|
||||||
|
{
|
||||||
|
if (modelNode.isValid())
|
||||||
|
m_modelNode = modelNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLiteral(QmlJS::AST::Node *ast)
|
||||||
|
{
|
||||||
|
if (QmlJS::AST::cast<QmlJS::AST::StringLiteral *>(ast)
|
||||||
|
|| QmlJS::AST::cast<QmlJS::AST::NumericLiteral *>(ast)
|
||||||
|
|| QmlJS::AST::cast<QmlJS::AST::TrueLiteral *>(ast)
|
||||||
|
|| QmlJS::AST::cast<QmlJS::AST::FalseLiteral *>(ast))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditor::prepareConnections()
|
||||||
|
{
|
||||||
|
if (!m_modelNode.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
BindingEditorWidget *bindingEditorWidget = m_dialog->bindingEditorWidget();
|
||||||
|
|
||||||
|
if (!bindingEditorWidget) {
|
||||||
|
qCInfo(ceLog) << Q_FUNC_INFO << "BindingEditorWidget is missing!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bindingEditorWidget->qmlJsEditorDocument()) {
|
||||||
|
qCInfo(ceLog) << Q_FUNC_INFO << "QmlJsEditorDocument is missing!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare objects for analysing slots
|
||||||
|
const QmlJSTools::SemanticInfo &semanticInfo = bindingEditorWidget->qmljsdocument->semanticInfo();
|
||||||
|
const QList<QmlJS::AST::Node *> path = semanticInfo.rangePath(0);
|
||||||
|
const QmlJS::ContextPtr &context = semanticInfo.context;
|
||||||
|
const QmlJS::ScopeChain &scopeChain = semanticInfo.scopeChain(path);
|
||||||
|
|
||||||
|
static QList<TypeName> typeWhiteList({"string",
|
||||||
|
"real", "int", "double",
|
||||||
|
"bool",
|
||||||
|
"QColor", "color",
|
||||||
|
"QtQuick.Item", "QQuickItem"});
|
||||||
|
|
||||||
|
static QList<PropertyName> methodBlackList({"toString", "destroy"});
|
||||||
|
|
||||||
|
QList<ActionEditorDialog::ConnectionOption> connections;
|
||||||
|
QList<ActionEditorDialog::SingletonOption> singletons;
|
||||||
|
QStringList states;
|
||||||
|
|
||||||
|
const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes();
|
||||||
|
for (const auto &modelNode : allNodes) {
|
||||||
|
// Skip nodes without specified id
|
||||||
|
if (!modelNode.hasId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ActionEditorDialog::ConnectionOption connection(modelNode.id());
|
||||||
|
|
||||||
|
for (const auto &propertyName : modelNode.metaInfo().propertyNames()) {
|
||||||
|
if (!typeWhiteList.contains(modelNode.metaInfo().propertyTypeName(propertyName)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString name = QString::fromUtf8(propertyName);
|
||||||
|
TypeName type = modelNode.metaInfo().propertyTypeName(propertyName);
|
||||||
|
if (type.contains("<cpp>."))
|
||||||
|
type.remove(0, 6);
|
||||||
|
|
||||||
|
connection.properties.append(ActionEditorDialog::PropertyOption(name, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const VariantProperty &variantProperty : modelNode.variantProperties()) {
|
||||||
|
if (variantProperty.isValid()) {
|
||||||
|
if (variantProperty.isDynamic()) {
|
||||||
|
if (!typeWhiteList.contains(variantProperty.dynamicTypeName()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString name = QString::fromUtf8(variantProperty.name());
|
||||||
|
TypeName type = variantProperty.dynamicTypeName();
|
||||||
|
if (type.contains("<cpp>."))
|
||||||
|
type.remove(0, 6);
|
||||||
|
|
||||||
|
connection.properties.append(ActionEditorDialog::PropertyOption(name, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &slotName : modelNode.metaInfo().slotNames()) {
|
||||||
|
QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(
|
||||||
|
QLatin1String("<expression>"), QmlJS::Dialect::JavaScript);
|
||||||
|
newDoc->setSource(QLatin1String(slotName));
|
||||||
|
newDoc->parseExpression();
|
||||||
|
|
||||||
|
QmlJS::AST::ExpressionNode *expression = newDoc->expression();
|
||||||
|
|
||||||
|
if (expression && !isLiteral(expression)) {
|
||||||
|
QmlJS::ValueOwner *interp = context->valueOwner();
|
||||||
|
const QmlJS::Value *value = interp->convertToObject(scopeChain.evaluate(expression));
|
||||||
|
|
||||||
|
if (const QmlJS::FunctionValue *f = value->asFunctionValue()) {
|
||||||
|
// Only add slots with zero arguments
|
||||||
|
if (f->namedArgumentCount() == 0 && !methodBlackList.contains(slotName))
|
||||||
|
connection.methods.append(QString::fromUtf8(slotName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.methods.removeDuplicates();
|
||||||
|
connections.append(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singletons
|
||||||
|
if (RewriterView *rv = m_modelNode.view()->rewriterView()) {
|
||||||
|
for (const QmlTypeData &data : rv->getQMLTypes()) {
|
||||||
|
if (!data.typeName.isEmpty()) {
|
||||||
|
NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo(data.typeName.toUtf8());
|
||||||
|
if (metaInfo.isValid()) {
|
||||||
|
ActionEditorDialog::SingletonOption singelton;
|
||||||
|
for (const PropertyName &propertyName : metaInfo.propertyNames()) {
|
||||||
|
TypeName type = metaInfo.propertyTypeName(propertyName);
|
||||||
|
|
||||||
|
if (!typeWhiteList.contains(type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString name = QString::fromUtf8(propertyName);
|
||||||
|
if (type.contains("<cpp>."))
|
||||||
|
type.remove(0, 6);
|
||||||
|
|
||||||
|
singelton.properties.append(ActionEditorDialog::PropertyOption(name, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!singelton.properties.isEmpty()) {
|
||||||
|
singelton.item = data.typeName;
|
||||||
|
singletons.append(singelton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// States
|
||||||
|
for (const QmlModelState &state : QmlItemNode(m_modelNode).states().allStates())
|
||||||
|
states.append(state.name());
|
||||||
|
|
||||||
|
|
||||||
|
if (!connections.isEmpty() && !m_dialog.isNull())
|
||||||
|
m_dialog->setAllConnections(connections, singletons, states);
|
||||||
|
}
|
||||||
|
|
||||||
void ActionEditor::updateWindowName()
|
void ActionEditor::updateWindowName()
|
||||||
{
|
{
|
||||||
if (!m_dialog.isNull())
|
if (!m_dialog.isNull()) {
|
||||||
{
|
m_dialog->setWindowTitle(m_dialog->defaultTitle());
|
||||||
m_dialog->setWindowTitle(tr("Connection Editor"));
|
|
||||||
m_dialog->raise();
|
m_dialog->raise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
#ifndef ACTIONEDITOR_H
|
#ifndef ACTIONEDITOR_H
|
||||||
#define ACTIONEDITOR_H
|
#define ACTIONEDITOR_H
|
||||||
|
|
||||||
#include <bindingeditor/bindingeditordialog.h>
|
#include <bindingeditor/actioneditordialog.h>
|
||||||
#include <qmldesignercorelib_global.h>
|
#include <qmldesignercorelib_global.h>
|
||||||
#include <modelnode.h>
|
#include <modelnode.h>
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ class ActionEditor : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(QString text READ bindingValue WRITE setBindingValue)
|
Q_PROPERTY(QString text READ connectionValue WRITE setConnectionValue)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ActionEditor(QObject *parent = nullptr);
|
ActionEditor(QObject *parent = nullptr);
|
||||||
@@ -52,14 +52,18 @@ public:
|
|||||||
Q_INVOKABLE void showWidget(int x, int y);
|
Q_INVOKABLE void showWidget(int x, int y);
|
||||||
Q_INVOKABLE void hideWidget();
|
Q_INVOKABLE void hideWidget();
|
||||||
|
|
||||||
QString bindingValue() const;
|
QString connectionValue() const;
|
||||||
void setBindingValue(const QString &text);
|
void setConnectionValue(const QString &text);
|
||||||
|
|
||||||
bool hasModelIndex() const;
|
bool hasModelIndex() const;
|
||||||
void resetModelIndex();
|
void resetModelIndex();
|
||||||
QModelIndex modelIndex() const;
|
QModelIndex modelIndex() const;
|
||||||
void setModelIndex(const QModelIndex &index);
|
void setModelIndex(const QModelIndex &index);
|
||||||
|
|
||||||
|
void setModelNode(const ModelNode &modelNode);
|
||||||
|
|
||||||
|
void prepareConnections();
|
||||||
|
|
||||||
Q_INVOKABLE void updateWindowName();
|
Q_INVOKABLE void updateWindowName();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -67,14 +71,12 @@ signals:
|
|||||||
void rejected();
|
void rejected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant backendValue() const;
|
|
||||||
QVariant modelNodeBackend() const;
|
|
||||||
QVariant stateModelNode() const;
|
|
||||||
void prepareDialog();
|
void prepareDialog();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<BindingEditorDialog> m_dialog;
|
QPointer<ActionEditorDialog> m_dialog;
|
||||||
QModelIndex m_index;
|
QModelIndex m_index;
|
||||||
|
QmlDesigner::ModelNode m_modelNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,656 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "actioneditordialog.h"
|
||||||
|
|
||||||
|
#include "connectionvisitor.h"
|
||||||
|
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <qmldesigner/qmldesignerplugin.h>
|
||||||
|
#include <qmljseditor/qmljseditor.h>
|
||||||
|
#include <qmljseditor/qmljseditordocument.h>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(ceLog, "qtc.qmldesigner.connectioneditor", QtWarningMsg)
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
ActionEditorDialog::ActionEditorDialog(QWidget *parent)
|
||||||
|
: AbstractEditorDialog(parent, tr("Connection Editor"))
|
||||||
|
{
|
||||||
|
setupUIComponents();
|
||||||
|
|
||||||
|
QObject::connect(m_comboBoxType, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::Type); });
|
||||||
|
|
||||||
|
// Action connections
|
||||||
|
QObject::connect(m_actionTargetItem, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetItem); });
|
||||||
|
QObject::connect(m_actionMethod, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetProperty); });
|
||||||
|
|
||||||
|
// Assignment connections
|
||||||
|
QObject::connect(m_assignmentTargetItem, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetItem); });
|
||||||
|
QObject::connect(m_assignmentTargetProperty, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetProperty); });
|
||||||
|
QObject::connect(m_assignmentSourceItem, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::SourceItem); });
|
||||||
|
QObject::connect(m_assignmentSourceProperty, QOverload<int>::of(&QComboBox::activated),
|
||||||
|
[this] (int idx) { this->updateComboBoxes(idx, ComboBox::SourceProperty); });
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionEditorDialog::~ActionEditorDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::adjustProperties()
|
||||||
|
{
|
||||||
|
// Analyze the current connection editor statement/expression
|
||||||
|
const auto qmlJSDocument = bindingEditorWidget()->qmlJsEditorDocument();
|
||||||
|
auto doc = QmlJS::Document::create(QLatin1String("<expression>"), QmlJS::Dialect::JavaScript);
|
||||||
|
doc->setSource(qmlJSDocument->plainText());
|
||||||
|
bool parseResult = doc->parseExpression();
|
||||||
|
|
||||||
|
if (!parseResult) {
|
||||||
|
qCInfo(ceLog) << Q_FUNC_INFO << "Couldn't parse the expression!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto astNode = doc->ast();
|
||||||
|
if (!astNode) {
|
||||||
|
qCInfo(ceLog) << Q_FUNC_INFO << "There was no AST::Node in the document!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionVisitor qmlVisitor;
|
||||||
|
QmlJS::AST::Node::accept(astNode, &qmlVisitor);
|
||||||
|
|
||||||
|
const auto expression = qmlVisitor.expression();
|
||||||
|
|
||||||
|
if (expression.isEmpty()) {
|
||||||
|
// Set all ComboBoxes to [Undefined], add connections to target item ComboBox
|
||||||
|
fillAndSetTargetItem(undefinedString);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(undefinedString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool typeDone = false;
|
||||||
|
bool targetDone = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < expression.count(); ++i) {
|
||||||
|
switch (expression[i].first) {
|
||||||
|
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_CallExpression:
|
||||||
|
{
|
||||||
|
setType(ConnectionType::Action);
|
||||||
|
typeDone = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_BinaryExpression:
|
||||||
|
{
|
||||||
|
setType(ConnectionType::Assignment);
|
||||||
|
typeDone = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_FieldMemberExpression:
|
||||||
|
{
|
||||||
|
QString fieldMember = expression[i].second;
|
||||||
|
++i;// Increment index to get IdentifierExpression or next FieldMemberExpression
|
||||||
|
while (expression[i].first == QmlJS::AST::Node::Kind::Kind_FieldMemberExpression) {
|
||||||
|
fieldMember.prepend(expression[i].second + ".");
|
||||||
|
++i; // Increment index to get IdentifierExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetDone && m_comboBoxType->currentIndex() != ConnectionType::Action) {
|
||||||
|
fillAndSetSourceItem(expression[i].second);
|
||||||
|
fillAndSetSourceProperty(fieldMember);
|
||||||
|
} else {
|
||||||
|
if (typeDone) {
|
||||||
|
fillAndSetTargetItem(expression[i].second);
|
||||||
|
fillAndSetTargetProperty(fieldMember);
|
||||||
|
} else { // e.g. 'element.width'
|
||||||
|
// In this case Assignment is more likley
|
||||||
|
setType(ConnectionType::Assignment);
|
||||||
|
fillAndSetTargetItem(expression[i].second);
|
||||||
|
fillAndSetTargetProperty(fieldMember);
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(undefinedString);
|
||||||
|
}
|
||||||
|
targetDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_TrueLiteral:
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_FalseLiteral:
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_NumericLiteral:
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_StringLiteral:
|
||||||
|
{
|
||||||
|
if (targetDone) {
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(expression[i].second, expression[i].first);
|
||||||
|
} else {
|
||||||
|
// In this case Assignment is more likley
|
||||||
|
setType(ConnectionType::Assignment);
|
||||||
|
fillAndSetTargetItem(undefinedString);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(expression[i].second, expression[i].first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QmlJS::AST::Node::Kind::Kind_IdentifierExpression:
|
||||||
|
{
|
||||||
|
if (typeDone) {
|
||||||
|
if (m_comboBoxType->currentIndex() == ConnectionType::Assignment) { // e.g. 'element = rectangle
|
||||||
|
if (targetDone) {
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(undefinedString);
|
||||||
|
} else {
|
||||||
|
fillAndSetTargetItem(expression[i].second);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
targetDone = true;
|
||||||
|
}
|
||||||
|
} else { // e.g. 'print("blabla")'
|
||||||
|
fillAndSetTargetItem(undefinedString);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
targetDone = true;
|
||||||
|
}
|
||||||
|
} else { // e.g. 'element'
|
||||||
|
// In this case Assignment is more likley
|
||||||
|
setType(ConnectionType::Assignment);
|
||||||
|
fillAndSetTargetItem(expression[i].second);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(undefinedString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
fillAndSetTargetItem(undefinedString);
|
||||||
|
fillAndSetTargetProperty(undefinedString);
|
||||||
|
fillAndSetSourceItem(undefinedString);
|
||||||
|
fillAndSetSourceProperty(undefinedString);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::setAllConnections(const QList<ConnectionOption> &connections,
|
||||||
|
const QList<SingletonOption> &singletons,
|
||||||
|
const QStringList &states)
|
||||||
|
{
|
||||||
|
m_lock = true;
|
||||||
|
|
||||||
|
m_connections = connections;
|
||||||
|
m_singletons = singletons;
|
||||||
|
m_states = states;
|
||||||
|
adjustProperties();
|
||||||
|
|
||||||
|
m_lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::updateComboBoxes(int index, ComboBox type)
|
||||||
|
{
|
||||||
|
Q_UNUSED(index)
|
||||||
|
|
||||||
|
const int currentType = m_comboBoxType->currentIndex();
|
||||||
|
const int currentStack = m_stackedLayout->currentIndex();
|
||||||
|
bool typeChanged = false;
|
||||||
|
|
||||||
|
if (type == ComboBox::Type) {
|
||||||
|
if (currentType != currentStack)
|
||||||
|
typeChanged = true;
|
||||||
|
else
|
||||||
|
return; // Prevent rebuild of expression if type didn't change
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeChanged) {
|
||||||
|
m_stackedLayout->setCurrentIndex(currentType);
|
||||||
|
if (currentStack == ConnectionType::Action) {
|
||||||
|
// Previous type was Action
|
||||||
|
const auto targetItem = m_actionTargetItem->currentText();
|
||||||
|
fillAndSetTargetItem(targetItem, true);
|
||||||
|
fillAndSetTargetProperty(QString(), true);
|
||||||
|
fillAndSetSourceItem(QString(), true);
|
||||||
|
fillAndSetSourceProperty(QString(), QmlJS::AST::Node::Kind::Kind_Undefined, true);
|
||||||
|
} else {
|
||||||
|
// Previous type was Assignment
|
||||||
|
const auto targetItem = m_assignmentTargetItem->currentText();
|
||||||
|
fillAndSetTargetItem(targetItem, true);
|
||||||
|
fillAndSetTargetProperty(QString(), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentType == ConnectionType::Action) {
|
||||||
|
// Prevent rebuild of expression if undefinedString item was selected
|
||||||
|
switch (type) {
|
||||||
|
case ComboBox::TargetItem:
|
||||||
|
if (m_actionTargetItem->currentText() == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case ComboBox::TargetProperty:
|
||||||
|
if (m_actionMethod->currentText() == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillAndSetTargetItem(m_actionTargetItem->currentText());
|
||||||
|
fillAndSetTargetProperty(m_actionMethod->currentText(), true);
|
||||||
|
} else { // ConnectionType::Assignment
|
||||||
|
const auto targetItem = m_assignmentTargetItem->currentText();
|
||||||
|
const auto targetProperty = m_assignmentTargetProperty->currentText();
|
||||||
|
const auto sourceItem = m_assignmentSourceItem->currentText();
|
||||||
|
const auto sourceProperty = m_assignmentSourceProperty->currentText();
|
||||||
|
|
||||||
|
// Prevent rebuild of expression if undefinedString item was selected
|
||||||
|
switch (type) {
|
||||||
|
case ComboBox::TargetItem:
|
||||||
|
if (targetItem == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case ComboBox::TargetProperty:
|
||||||
|
if (targetProperty == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case ComboBox::SourceItem:
|
||||||
|
if (sourceItem == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case ComboBox::SourceProperty:
|
||||||
|
if (sourceProperty == undefinedString)
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillAndSetTargetItem(targetItem, true);
|
||||||
|
fillAndSetTargetProperty(targetProperty, true);
|
||||||
|
|
||||||
|
const auto sourcePropertyType = m_assignmentSourceProperty->currentData().value<TypeName>();
|
||||||
|
|
||||||
|
if (type == ComboBox::SourceItem) {
|
||||||
|
fillAndSetSourceItem(sourceItem, true);
|
||||||
|
|
||||||
|
if (sourcePropertyType == specificItem)
|
||||||
|
fillAndSetSourceProperty(QString(),
|
||||||
|
QmlJS::AST::Node::Kind::Kind_Undefined,
|
||||||
|
true);
|
||||||
|
else
|
||||||
|
fillAndSetSourceProperty(sourceProperty,
|
||||||
|
QmlJS::AST::Node::Kind::Kind_Undefined,
|
||||||
|
true);
|
||||||
|
} else if (type == ComboBox::SourceProperty) {
|
||||||
|
if (sourcePropertyType == specificItem) {
|
||||||
|
fillAndSetSourceItem(QString(), false);
|
||||||
|
fillAndSetSourceProperty(sourceProperty,
|
||||||
|
QmlJS::AST::Node::Kind::Kind_StringLiteral,
|
||||||
|
false);
|
||||||
|
} else {
|
||||||
|
fillAndSetSourceProperty(sourceProperty);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sourcePropertyType == specificItem) {
|
||||||
|
fillAndSetSourceItem(QString(), false);
|
||||||
|
fillAndSetSourceProperty(sourceProperty,
|
||||||
|
QmlJS::AST::Node::Kind::Kind_StringLiteral,
|
||||||
|
false);
|
||||||
|
} else {
|
||||||
|
fillAndSetSourceItem(sourceItem, true);
|
||||||
|
fillAndSetSourceProperty(sourceProperty,
|
||||||
|
QmlJS::AST::Node::Kind::Kind_Undefined,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compose expression
|
||||||
|
QString value;
|
||||||
|
if (currentType == ConnectionType::Action) {
|
||||||
|
const auto targetItem = m_actionTargetItem->currentText();
|
||||||
|
const auto method = m_actionMethod->currentText();
|
||||||
|
|
||||||
|
if (targetItem != undefinedString && method != undefinedString){
|
||||||
|
value = targetItem + "." + method + "()";
|
||||||
|
} else if (targetItem != undefinedString && method == undefinedString) {
|
||||||
|
value = targetItem;
|
||||||
|
}
|
||||||
|
} else { // ConnectionType::Assignment
|
||||||
|
const auto targetItem = m_assignmentTargetItem->currentText();
|
||||||
|
const auto targetProperty = m_assignmentTargetProperty->currentText();
|
||||||
|
const auto sourceItem = m_assignmentSourceItem->currentText();
|
||||||
|
const auto sourceProperty = m_assignmentSourceProperty->currentText();
|
||||||
|
|
||||||
|
QString lhs;
|
||||||
|
|
||||||
|
if (targetItem != undefinedString && targetProperty != undefinedString) {
|
||||||
|
lhs = targetItem + "." + targetProperty;
|
||||||
|
} else if (targetItem != undefinedString && targetProperty == undefinedString) {
|
||||||
|
lhs = targetItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString rhs;
|
||||||
|
|
||||||
|
if (sourceItem != undefinedString && sourceProperty != undefinedString) {
|
||||||
|
rhs = sourceItem + "." + sourceProperty;
|
||||||
|
} else if (sourceItem != undefinedString && sourceProperty == undefinedString) {
|
||||||
|
rhs = sourceItem;
|
||||||
|
} else if (sourceItem == undefinedString && sourceProperty != undefinedString) {
|
||||||
|
const QString data = m_assignmentTargetProperty->currentData().toString();
|
||||||
|
if (data == "string") {
|
||||||
|
rhs = "\"" + sourceProperty + "\"";
|
||||||
|
} else {
|
||||||
|
rhs = sourceProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lhs.isEmpty() && !rhs.isEmpty()) {
|
||||||
|
value = lhs + " = " + rhs;
|
||||||
|
} else {
|
||||||
|
value = lhs + rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const QSignalBlocker blocker(m_editorWidget);
|
||||||
|
setEditorValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::setupUIComponents()
|
||||||
|
{
|
||||||
|
m_comboBoxType = new QComboBox(this);
|
||||||
|
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<ConnectionType>();
|
||||||
|
for (int i = 0; i != metaEnum.keyCount(); ++i) {
|
||||||
|
const char *key = QMetaEnum::fromType<ConnectionType>().valueToKey(i);
|
||||||
|
m_comboBoxType->addItem(QString::fromLatin1(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_comboBoxLayout->addWidget(m_comboBoxType);
|
||||||
|
|
||||||
|
m_stackedLayout = new QStackedLayout();
|
||||||
|
|
||||||
|
m_actionLayout = new QHBoxLayout();
|
||||||
|
m_assignmentLayout = new QHBoxLayout();
|
||||||
|
|
||||||
|
m_actionPlaceholder = new QWidget(this);
|
||||||
|
m_actionPlaceholder->setLayout(m_actionLayout);
|
||||||
|
|
||||||
|
m_assignmentPlaceholder = new QWidget(this);
|
||||||
|
m_assignmentPlaceholder->setLayout(m_assignmentLayout);
|
||||||
|
|
||||||
|
// Setup action ComboBoxes
|
||||||
|
m_actionTargetItem = new QComboBox(this);
|
||||||
|
m_actionMethod = new QComboBox(this);
|
||||||
|
m_actionLayout->addWidget(m_actionTargetItem);
|
||||||
|
m_actionLayout->addWidget(m_actionMethod);
|
||||||
|
|
||||||
|
// Setup assignment ComboBoxes
|
||||||
|
m_assignmentTargetItem = new QComboBox(this);
|
||||||
|
m_assignmentTargetProperty = new QComboBox(this);
|
||||||
|
m_assignmentSourceItem = new QComboBox(this);
|
||||||
|
m_assignmentSourceProperty = new QComboBox(this);
|
||||||
|
m_assignmentLayout->addWidget(m_assignmentTargetItem);
|
||||||
|
m_assignmentLayout->addWidget(m_assignmentTargetProperty);
|
||||||
|
m_assignmentLayout->addWidget(m_assignmentSourceItem);
|
||||||
|
m_assignmentLayout->addWidget(m_assignmentSourceProperty);
|
||||||
|
|
||||||
|
m_stackedLayout->addWidget(m_actionPlaceholder);
|
||||||
|
m_stackedLayout->addWidget(m_assignmentPlaceholder);
|
||||||
|
|
||||||
|
m_comboBoxLayout->addItem(m_stackedLayout);
|
||||||
|
|
||||||
|
this->resize(720, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::setType(ConnectionType type)
|
||||||
|
{
|
||||||
|
m_comboBoxType->setCurrentIndex(type);
|
||||||
|
m_stackedLayout->setCurrentIndex(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::fillAndSetTargetItem(const QString &value, bool useDefault)
|
||||||
|
{
|
||||||
|
if (m_comboBoxType->currentIndex() == ConnectionType::Action) {
|
||||||
|
m_actionTargetItem->clear();
|
||||||
|
for (const auto &connection : m_connections) {
|
||||||
|
if (!connection.methods.isEmpty())
|
||||||
|
m_actionTargetItem->addItem(connection.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_actionTargetItem->findText(value) != -1) {
|
||||||
|
m_actionTargetItem->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_actionTargetItem->count())
|
||||||
|
m_actionTargetItem->setCurrentIndex(0);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_actionTargetItem);
|
||||||
|
}
|
||||||
|
} else { // ConnectionType::Assignment
|
||||||
|
m_assignmentTargetItem->clear();
|
||||||
|
for (const auto &connection : m_connections) {
|
||||||
|
if (!connection.properties.isEmpty())
|
||||||
|
m_assignmentTargetItem->addItem(connection.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_assignmentTargetItem->findText(value) != -1) {
|
||||||
|
m_assignmentTargetItem->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_actionTargetItem->count())
|
||||||
|
m_actionTargetItem->setCurrentIndex(0);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentTargetItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::fillAndSetTargetProperty(const QString &value, bool useDefault)
|
||||||
|
{
|
||||||
|
if (m_comboBoxType->currentIndex() == ConnectionType::Action) {
|
||||||
|
m_actionMethod->clear();
|
||||||
|
const QString targetItem = m_actionTargetItem->currentText();
|
||||||
|
const int idx = m_connections.indexOf(targetItem);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
insertAndSetUndefined(m_actionMethod);
|
||||||
|
} else {
|
||||||
|
m_actionMethod->addItems(m_connections[idx].methods);
|
||||||
|
|
||||||
|
if (m_actionMethod->findText(value) != -1) {
|
||||||
|
m_actionMethod->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_actionMethod->count())
|
||||||
|
m_actionMethod->setCurrentIndex(0);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_actionMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // ConnectionType::Assignment
|
||||||
|
m_assignmentTargetProperty->clear();
|
||||||
|
const QString targetItem = m_assignmentTargetItem->currentText();
|
||||||
|
const int idx = m_connections.indexOf(targetItem);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
insertAndSetUndefined(m_assignmentTargetProperty);
|
||||||
|
} else {
|
||||||
|
for (const auto &property : m_connections[idx].properties)
|
||||||
|
m_assignmentTargetProperty->addItem(property.name, property.type);
|
||||||
|
|
||||||
|
if (m_assignmentTargetProperty->findText(value) != -1) {
|
||||||
|
m_assignmentTargetProperty->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_assignmentTargetProperty->count())
|
||||||
|
m_assignmentTargetProperty->setCurrentIndex(0);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentTargetProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::fillAndSetSourceItem(const QString &value, bool useDefault)
|
||||||
|
{
|
||||||
|
m_assignmentSourceItem->clear();
|
||||||
|
const TypeName targetPropertyType = m_assignmentTargetProperty->currentData().value<TypeName>();
|
||||||
|
|
||||||
|
if (!targetPropertyType.isEmpty()) {
|
||||||
|
for (const ConnectionOption &connection : m_connections) {
|
||||||
|
if (!connection.containsType(targetPropertyType))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_assignmentSourceItem->addItem(connection.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Constants
|
||||||
|
for (const SingletonOption &singleton : m_singletons) {
|
||||||
|
if (!singleton.containsType(targetPropertyType))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_assignmentSourceItem->addItem(singleton.item, singletonItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_assignmentSourceItem->findText(value) != -1) {
|
||||||
|
m_assignmentSourceItem->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_assignmentSourceItem->count())
|
||||||
|
m_assignmentSourceItem->setCurrentIndex(0);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentSourceItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::fillAndSetSourceProperty(const QString &value,
|
||||||
|
QmlJS::AST::Node::Kind kind,
|
||||||
|
bool useDefault)
|
||||||
|
{
|
||||||
|
m_assignmentSourceProperty->clear();
|
||||||
|
const TypeName targetPropertyType = m_assignmentTargetProperty->currentData().value<TypeName>();
|
||||||
|
const QString targetProperty = m_assignmentTargetProperty->currentText();
|
||||||
|
|
||||||
|
if (kind != QmlJS::AST::Node::Kind::Kind_Undefined) {
|
||||||
|
if (targetPropertyType == "bool") {
|
||||||
|
m_assignmentSourceProperty->addItem("true", specificItem);
|
||||||
|
m_assignmentSourceProperty->addItem("false", specificItem);
|
||||||
|
|
||||||
|
if (m_assignmentSourceProperty->findText(value) != -1)
|
||||||
|
m_assignmentSourceProperty->setCurrentText(value);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentSourceProperty);
|
||||||
|
} else if (targetProperty == "state") {
|
||||||
|
for (const auto &state : m_states)
|
||||||
|
m_assignmentSourceProperty->addItem(state, specificItem);
|
||||||
|
|
||||||
|
if (m_assignmentSourceProperty->findText(value) != -1)
|
||||||
|
m_assignmentSourceProperty->setCurrentText(value);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentSourceProperty);
|
||||||
|
} else {
|
||||||
|
m_assignmentSourceProperty->insertItem(0, value, specificItem);
|
||||||
|
m_assignmentSourceProperty->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const TypeName sourceItemType = m_assignmentSourceItem->currentData().value<TypeName>();
|
||||||
|
const QString sourceItem = m_assignmentSourceItem->currentText();
|
||||||
|
// We need to distinguish between singleton (Constants) and standard item
|
||||||
|
const int idx = (sourceItemType == singletonItem) ? m_singletons.indexOf(sourceItem)
|
||||||
|
: m_connections.indexOf(sourceItem);
|
||||||
|
|
||||||
|
if (idx == -1) {
|
||||||
|
insertAndSetUndefined(m_assignmentSourceProperty);
|
||||||
|
} else {
|
||||||
|
int specificsEnd = -1;
|
||||||
|
// Add type specific items
|
||||||
|
if (targetPropertyType == "bool") {
|
||||||
|
m_assignmentSourceProperty->addItem("true", specificItem);
|
||||||
|
m_assignmentSourceProperty->addItem("false", specificItem);
|
||||||
|
specificsEnd = 2;
|
||||||
|
} else if (targetProperty == "state") {
|
||||||
|
for (const auto &state : m_states)
|
||||||
|
m_assignmentSourceProperty->addItem(state, specificItem);
|
||||||
|
|
||||||
|
specificsEnd = m_states.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specificsEnd != -1)
|
||||||
|
m_assignmentSourceProperty->insertSeparator(specificsEnd);
|
||||||
|
|
||||||
|
if (sourceItemType == singletonItem) {
|
||||||
|
for (const auto &property : m_singletons[idx].properties) {
|
||||||
|
if (targetPropertyType.isEmpty() // TODO isEmpty correct?!
|
||||||
|
|| property.type == targetPropertyType
|
||||||
|
|| (isNumeric(property.type) && isNumeric(targetPropertyType)))
|
||||||
|
m_assignmentSourceProperty->addItem(property.name, property.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const auto &property : m_connections[idx].properties) {
|
||||||
|
if (targetPropertyType.isEmpty() // TODO isEmpty correct?!
|
||||||
|
|| property.type == targetPropertyType
|
||||||
|
|| (isNumeric(property.type) && isNumeric(targetPropertyType)))
|
||||||
|
m_assignmentSourceProperty->addItem(property.name, property.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_assignmentSourceProperty->findText(value) != -1 && !value.isEmpty()) {
|
||||||
|
m_assignmentSourceProperty->setCurrentText(value);
|
||||||
|
} else {
|
||||||
|
if (useDefault && m_assignmentSourceProperty->count())
|
||||||
|
m_assignmentSourceProperty->setCurrentIndex(specificsEnd + 1);
|
||||||
|
else
|
||||||
|
insertAndSetUndefined(m_assignmentSourceProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionEditorDialog::insertAndSetUndefined(QComboBox *comboBox)
|
||||||
|
{
|
||||||
|
comboBox->insertItem(0, undefinedString);
|
||||||
|
comboBox->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QmlDesigner namespace
|
@@ -0,0 +1,155 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ACTIONEDITORDIALOG_H
|
||||||
|
#define ACTIONEDITORDIALOG_H
|
||||||
|
|
||||||
|
#include <bindingeditor/abstracteditordialog.h>
|
||||||
|
#include <qmljs/parser/qmljsast_p.h>
|
||||||
|
|
||||||
|
#include <QStackedLayout>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QComboBox;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class ActionEditorDialog : public AbstractEditorDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ConnectionType { Action, Assignment };
|
||||||
|
Q_ENUM(ConnectionType)
|
||||||
|
|
||||||
|
enum ComboBox { Type, TargetItem, TargetProperty, SourceItem, SourceProperty };
|
||||||
|
Q_ENUM(ComboBox)
|
||||||
|
|
||||||
|
class PropertyOption
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PropertyOption() {}
|
||||||
|
PropertyOption(const QString &n, const TypeName &t)
|
||||||
|
: name(n)
|
||||||
|
, type(t)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const QString &value) const { return value == name; }
|
||||||
|
bool operator==(const PropertyOption &value) const { return value.name == name; }
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
TypeName type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SingletonOption
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SingletonOption() {}
|
||||||
|
SingletonOption(const QString &value) { item = value; }
|
||||||
|
|
||||||
|
bool containsType(const TypeName &t) const
|
||||||
|
{
|
||||||
|
for (const auto &p : properties) {
|
||||||
|
if (t == p.type || (isNumeric(t) && isNumeric(p.type)))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const QString &value) const { return value == item; }
|
||||||
|
bool operator==(const SingletonOption &value) const { return value.item == item; }
|
||||||
|
|
||||||
|
QString item;
|
||||||
|
QList<PropertyOption> properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConnectionOption : public SingletonOption
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConnectionOption() {}
|
||||||
|
ConnectionOption(const QString &value) : SingletonOption(value) {}
|
||||||
|
|
||||||
|
QStringList methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ActionEditorDialog(QWidget *parent = nullptr);
|
||||||
|
~ActionEditorDialog() override;
|
||||||
|
|
||||||
|
void adjustProperties() override;
|
||||||
|
|
||||||
|
void setAllConnections(const QList<ConnectionOption> &connections,
|
||||||
|
const QList<SingletonOption> &singeltons,
|
||||||
|
const QStringList &states);
|
||||||
|
|
||||||
|
void updateComboBoxes(int idx, ComboBox type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUIComponents();
|
||||||
|
|
||||||
|
void setType(ConnectionType type);
|
||||||
|
|
||||||
|
void fillAndSetTargetItem(const QString &value, bool useDefault = false);
|
||||||
|
void fillAndSetTargetProperty(const QString &value, bool useDefault = false);
|
||||||
|
|
||||||
|
void fillAndSetSourceItem(const QString &value, bool useDefault = false);
|
||||||
|
void fillAndSetSourceProperty(const QString &value,
|
||||||
|
QmlJS::AST::Node::Kind kind = QmlJS::AST::Node::Kind::Kind_Undefined,
|
||||||
|
bool useDefault = false);
|
||||||
|
|
||||||
|
void insertAndSetUndefined(QComboBox *comboBox);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *m_comboBoxType = nullptr;
|
||||||
|
|
||||||
|
QStackedLayout *m_stackedLayout = nullptr;
|
||||||
|
|
||||||
|
QWidget *m_actionPlaceholder = nullptr;
|
||||||
|
QWidget *m_assignmentPlaceholder = nullptr;
|
||||||
|
|
||||||
|
QHBoxLayout *m_actionLayout = nullptr;
|
||||||
|
QHBoxLayout *m_assignmentLayout = nullptr;
|
||||||
|
|
||||||
|
QComboBox *m_actionTargetItem = nullptr;
|
||||||
|
QComboBox *m_actionMethod = nullptr;
|
||||||
|
|
||||||
|
QComboBox *m_assignmentTargetItem = nullptr;
|
||||||
|
QComboBox *m_assignmentTargetProperty = nullptr;
|
||||||
|
QComboBox *m_assignmentSourceItem = nullptr;
|
||||||
|
QComboBox *m_assignmentSourceProperty = nullptr; // Value
|
||||||
|
|
||||||
|
QList<ConnectionOption> m_connections;
|
||||||
|
QList<SingletonOption> m_singletons;
|
||||||
|
QStringList m_states;
|
||||||
|
|
||||||
|
const TypeName specificItem = {"specific"};
|
||||||
|
const TypeName singletonItem = {"singleton"};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ACTIONEDITORDIALOG_H
|
@@ -28,6 +28,7 @@
|
|||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
|
#include <bindingeditor/bindingeditordialog.h>
|
||||||
|
|
||||||
#include <metainfo.h>
|
#include <metainfo.h>
|
||||||
#include <qmlmodelnodeproxy.h>
|
#include <qmlmodelnodeproxy.h>
|
||||||
@@ -60,14 +61,14 @@ void BindingEditor::prepareDialog()
|
|||||||
{
|
{
|
||||||
if (s_lastBindingEditor)
|
if (s_lastBindingEditor)
|
||||||
s_lastBindingEditor->hideWidget();
|
s_lastBindingEditor->hideWidget();
|
||||||
|
|
||||||
s_lastBindingEditor = this;
|
s_lastBindingEditor = this;
|
||||||
|
|
||||||
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent());
|
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent());
|
||||||
|
|
||||||
|
QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
|
||||||
QObject::connect(m_dialog, &BindingEditorDialog::accepted,
|
|
||||||
this, &BindingEditor::accepted);
|
this, &BindingEditor::accepted);
|
||||||
QObject::connect(m_dialog, &BindingEditorDialog::rejected,
|
QObject::connect(m_dialog, &AbstractEditorDialog::rejected,
|
||||||
this, &BindingEditor::rejected);
|
this, &BindingEditor::rejected);
|
||||||
|
|
||||||
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
@@ -89,8 +90,8 @@ void BindingEditor::hideWidget()
|
|||||||
{
|
{
|
||||||
if (s_lastBindingEditor == this)
|
if (s_lastBindingEditor == this)
|
||||||
s_lastBindingEditor = nullptr;
|
s_lastBindingEditor = nullptr;
|
||||||
if (m_dialog)
|
|
||||||
{
|
if (m_dialog) {
|
||||||
m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override
|
m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override
|
||||||
m_dialog->close();
|
m_dialog->close();
|
||||||
}
|
}
|
||||||
@@ -118,8 +119,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
|
|||||||
const PropertyEditorValue *propertyEditorValue = qobject_cast<const PropertyEditorValue *>(backendValueObj);
|
const PropertyEditorValue *propertyEditorValue = qobject_cast<const PropertyEditorValue *>(backendValueObj);
|
||||||
const ModelNode node = propertyEditorValue->modelNode();
|
const ModelNode node = propertyEditorValue->modelNode();
|
||||||
|
|
||||||
if (node.isValid())
|
if (node.isValid()) {
|
||||||
{
|
|
||||||
m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name());
|
m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name());
|
||||||
|
|
||||||
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown")
|
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown")
|
||||||
@@ -141,9 +141,8 @@ void BindingEditor::setModelNodeBackend(const QVariant &modelNodeBackend)
|
|||||||
const auto backendObjectCasted =
|
const auto backendObjectCasted =
|
||||||
qobject_cast<const QmlDesigner::QmlModelNodeProxy *>(modelNodeBackendObject);
|
qobject_cast<const QmlDesigner::QmlModelNodeProxy *>(modelNodeBackendObject);
|
||||||
|
|
||||||
if (backendObjectCasted) {
|
if (backendObjectCasted)
|
||||||
m_modelNode = backendObjectCasted->qmlObjectNode().modelNode();
|
m_modelNode = backendObjectCasted->qmlObjectNode().modelNode();
|
||||||
}
|
|
||||||
|
|
||||||
emit modelNodeBackendChanged();
|
emit modelNodeBackendChanged();
|
||||||
}
|
}
|
||||||
@@ -151,8 +150,7 @@ void BindingEditor::setModelNodeBackend(const QVariant &modelNodeBackend)
|
|||||||
|
|
||||||
void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
|
void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
|
||||||
{
|
{
|
||||||
if (stateModelNode.isValid())
|
if (stateModelNode.isValid()) {
|
||||||
{
|
|
||||||
m_stateModelNode = stateModelNode;
|
m_stateModelNode = stateModelNode;
|
||||||
m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>();
|
m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>();
|
||||||
|
|
||||||
@@ -188,21 +186,21 @@ void BindingEditor::prepareBindings()
|
|||||||
const QList<TypeName> variantTypes = {"alias", "unknown", "variant", "var"};
|
const QList<TypeName> variantTypes = {"alias", "unknown", "variant", "var"};
|
||||||
const QList<TypeName> numericTypes = {"double", "real", "int"};
|
const QList<TypeName> numericTypes = {"double", "real", "int"};
|
||||||
const QList<TypeName> colorTypes = {"QColor", "color"};
|
const QList<TypeName> colorTypes = {"QColor", "color"};
|
||||||
auto isNumeric = [&numericTypes](TypeName compareType) { return numericTypes.contains(compareType); };
|
auto isVariant = [&variantTypes](const TypeName &compareType) { return variantTypes.contains(compareType); };
|
||||||
auto isColor = [&colorTypes](TypeName compareType) { return colorTypes.contains(compareType); };
|
auto isNumeric = [&numericTypes](const TypeName &compareType) { return numericTypes.contains(compareType); };
|
||||||
|
auto isColor = [&colorTypes](const TypeName &compareType) { return colorTypes.contains(compareType); };
|
||||||
|
|
||||||
const bool skipTypeFiltering = variantTypes.contains(m_backendValueTypeName);
|
const bool skipTypeFiltering = isVariant(m_backendValueTypeName);
|
||||||
const bool targetTypeIsNumeric = isNumeric(m_backendValueTypeName);
|
const bool targetTypeIsNumeric = isNumeric(m_backendValueTypeName);
|
||||||
|
|
||||||
for (const auto &objnode : allNodes) {
|
for (const auto &objnode : allNodes) {
|
||||||
BindingEditorDialog::BindingOption binding;
|
BindingEditorDialog::BindingOption binding;
|
||||||
for (const auto &propertyName : objnode.metaInfo().propertyNames())
|
for (const auto &propertyName : objnode.metaInfo().propertyNames()) {
|
||||||
{
|
|
||||||
TypeName propertyTypeName = objnode.metaInfo().propertyTypeName(propertyName);
|
TypeName propertyTypeName = objnode.metaInfo().propertyTypeName(propertyName);
|
||||||
|
|
||||||
if (skipTypeFiltering
|
if (skipTypeFiltering
|
||||||
|| (m_backendValueTypeName == propertyTypeName)
|
|| (m_backendValueTypeName == propertyTypeName)
|
||||||
|| variantTypes.contains(propertyTypeName)
|
|| isVariant(propertyTypeName)
|
||||||
|| (targetTypeIsNumeric && isNumeric(propertyTypeName))) {
|
|| (targetTypeIsNumeric && isNumeric(propertyTypeName))) {
|
||||||
binding.properties.append(QString::fromUtf8(propertyName));
|
binding.properties.append(QString::fromUtf8(propertyName));
|
||||||
}
|
}
|
||||||
@@ -215,7 +213,7 @@ void BindingEditor::prepareBindings()
|
|||||||
const TypeName dynamicTypeName = bindingProperty.dynamicTypeName();
|
const TypeName dynamicTypeName = bindingProperty.dynamicTypeName();
|
||||||
if (skipTypeFiltering
|
if (skipTypeFiltering
|
||||||
|| (dynamicTypeName == m_backendValueTypeName)
|
|| (dynamicTypeName == m_backendValueTypeName)
|
||||||
|| variantTypes.contains(dynamicTypeName)
|
|| isVariant(dynamicTypeName)
|
||||||
|| (targetTypeIsNumeric && isNumeric(dynamicTypeName))) {
|
|| (targetTypeIsNumeric && isNumeric(dynamicTypeName))) {
|
||||||
binding.properties.append(QString::fromUtf8(bindingProperty.name()));
|
binding.properties.append(QString::fromUtf8(bindingProperty.name()));
|
||||||
}
|
}
|
||||||
@@ -228,7 +226,7 @@ void BindingEditor::prepareBindings()
|
|||||||
const TypeName dynamicTypeName = variantProperty.dynamicTypeName();
|
const TypeName dynamicTypeName = variantProperty.dynamicTypeName();
|
||||||
if (skipTypeFiltering
|
if (skipTypeFiltering
|
||||||
|| (dynamicTypeName == m_backendValueTypeName)
|
|| (dynamicTypeName == m_backendValueTypeName)
|
||||||
|| variantTypes.contains(dynamicTypeName)
|
|| isVariant(dynamicTypeName)
|
||||||
|| (targetTypeIsNumeric && isNumeric(dynamicTypeName))) {
|
|| (targetTypeIsNumeric && isNumeric(dynamicTypeName))) {
|
||||||
binding.properties.append(QString::fromUtf8(variantProperty.name()));
|
binding.properties.append(QString::fromUtf8(variantProperty.name()));
|
||||||
}
|
}
|
||||||
@@ -256,7 +254,7 @@ void BindingEditor::prepareBindings()
|
|||||||
|
|
||||||
if (skipTypeFiltering
|
if (skipTypeFiltering
|
||||||
|| (m_backendValueTypeName == propertyTypeName)
|
|| (m_backendValueTypeName == propertyTypeName)
|
||||||
|| (variantTypes.contains(propertyTypeName))
|
|| (isVariant(propertyTypeName))
|
||||||
|| (targetTypeIsNumeric && isNumeric(propertyTypeName))
|
|| (targetTypeIsNumeric && isNumeric(propertyTypeName))
|
||||||
|| (isColor(m_backendValueTypeName) && isColor(propertyTypeName))) {
|
|| (isColor(m_backendValueTypeName) && isColor(propertyTypeName))) {
|
||||||
binding.properties.append(QString::fromUtf8(propertyName));
|
binding.properties.append(QString::fromUtf8(propertyName));
|
||||||
@@ -281,10 +279,8 @@ void BindingEditor::prepareBindings()
|
|||||||
void BindingEditor::updateWindowName()
|
void BindingEditor::updateWindowName()
|
||||||
{
|
{
|
||||||
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty())
|
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty())
|
||||||
{
|
|
||||||
m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]");
|
m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QVariant BindingEditor::backendValue() const
|
QVariant BindingEditor::backendValue() const
|
||||||
{
|
{
|
||||||
|
@@ -1,9 +1,15 @@
|
|||||||
HEADERS += $$PWD/bindingeditor.h
|
HEADERS += $$PWD/bindingeditor.h
|
||||||
HEADERS += $$PWD/actioneditor.h
|
HEADERS += $$PWD/actioneditor.h
|
||||||
|
HEADERS += $$PWD/abstracteditordialog.h
|
||||||
|
HEADERS += $$PWD/actioneditordialog.h
|
||||||
HEADERS += $$PWD/bindingeditordialog.h
|
HEADERS += $$PWD/bindingeditordialog.h
|
||||||
HEADERS += $$PWD/bindingeditorwidget.h
|
HEADERS += $$PWD/bindingeditorwidget.h
|
||||||
|
HEADERS += $$PWD/connectionvisitor.h
|
||||||
|
|
||||||
SOURCES += $$PWD/bindingeditor.cpp
|
SOURCES += $$PWD/bindingeditor.cpp
|
||||||
SOURCES += $$PWD/actioneditor.cpp
|
SOURCES += $$PWD/actioneditor.cpp
|
||||||
|
SOURCES += $$PWD/abstracteditordialog.cpp
|
||||||
|
SOURCES += $$PWD/actioneditordialog.cpp
|
||||||
SOURCES += $$PWD/bindingeditordialog.cpp
|
SOURCES += $$PWD/bindingeditordialog.cpp
|
||||||
SOURCES += $$PWD/bindingeditorwidget.cpp
|
SOURCES += $$PWD/bindingeditorwidget.cpp
|
||||||
|
SOURCES += $$PWD/connectionvisitor.cpp
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2019 The Qt Company Ltd.
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -41,80 +41,19 @@
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
BindingEditorDialog::BindingEditorDialog(QWidget *parent, DialogType type)
|
BindingEditorDialog::BindingEditorDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: AbstractEditorDialog(parent, tr("Binding Editor"))
|
||||||
, m_dialogType(type)
|
|
||||||
{
|
{
|
||||||
setWindowFlag(Qt::Tool, true);
|
|
||||||
setWindowTitle(defaultTitle());
|
|
||||||
setModal(false);
|
|
||||||
|
|
||||||
setupJSEditor();
|
|
||||||
setupUIComponents();
|
setupUIComponents();
|
||||||
|
|
||||||
QObject::connect(m_buttonBox, &QDialogButtonBox::accepted,
|
|
||||||
this, &BindingEditorDialog::accepted);
|
|
||||||
QObject::connect(m_buttonBox, &QDialogButtonBox::rejected,
|
|
||||||
this, &BindingEditorDialog::rejected);
|
|
||||||
QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked,
|
|
||||||
this, &BindingEditorDialog::accepted);
|
|
||||||
|
|
||||||
if (m_dialogType == DialogType::BindingDialog) {
|
|
||||||
QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||||
this, &BindingEditorDialog::itemIDChanged);
|
this, &BindingEditorDialog::itemIDChanged);
|
||||||
QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||||
this, &BindingEditorDialog::propertyIDChanged);
|
this, &BindingEditorDialog::propertyIDChanged);
|
||||||
QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged,
|
|
||||||
this, &BindingEditorDialog::textChanged);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BindingEditorDialog::~BindingEditorDialog()
|
BindingEditorDialog::~BindingEditorDialog()
|
||||||
{
|
{
|
||||||
delete m_editor; //m_editorWidget is handled by basetexteditor destructor
|
|
||||||
delete m_buttonBox;
|
|
||||||
delete m_comboBoxItem;
|
|
||||||
delete m_comboBoxProperty;
|
|
||||||
delete m_comboBoxLayout;
|
|
||||||
delete m_verticalLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindingEditorDialog::showWidget()
|
|
||||||
{
|
|
||||||
this->show();
|
|
||||||
this->raise();
|
|
||||||
m_editorWidget->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindingEditorDialog::showWidget(int x, int y)
|
|
||||||
{
|
|
||||||
showWidget();
|
|
||||||
move(QPoint(x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BindingEditorDialog::editorValue() const
|
|
||||||
{
|
|
||||||
if (!m_editorWidget)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return m_editorWidget->document()->toPlainText();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindingEditorDialog::setEditorValue(const QString &text)
|
|
||||||
{
|
|
||||||
if (m_editorWidget)
|
|
||||||
m_editorWidget->document()->setPlainText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindingEditorDialog::setAllBindings(QList<BindingEditorDialog::BindingOption> bindings)
|
|
||||||
{
|
|
||||||
m_lock = true;
|
|
||||||
|
|
||||||
m_bindings = bindings;
|
|
||||||
setupComboBoxes();
|
|
||||||
adjustProperties();
|
|
||||||
|
|
||||||
m_lock = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingEditorDialog::adjustProperties()
|
void BindingEditorDialog::adjustProperties()
|
||||||
@@ -155,69 +94,26 @@ void BindingEditorDialog::adjustProperties()
|
|||||||
m_comboBoxProperty->setCurrentText(property);
|
m_comboBoxProperty->setCurrentText(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingEditorDialog::unregisterAutoCompletion()
|
void BindingEditorDialog::setAllBindings(QList<BindingOption> bindings)
|
||||||
{
|
{
|
||||||
if (m_editorWidget)
|
m_lock = true;
|
||||||
m_editorWidget->unregisterAutoCompletion();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString BindingEditorDialog::defaultTitle() const
|
m_bindings = bindings;
|
||||||
{
|
setupComboBoxes();
|
||||||
return titleString;
|
adjustProperties();
|
||||||
}
|
|
||||||
|
|
||||||
void BindingEditorDialog::setupJSEditor()
|
m_lock = false;
|
||||||
{
|
|
||||||
static BindingEditorFactory f;
|
|
||||||
m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor());
|
|
||||||
m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget());
|
|
||||||
|
|
||||||
Core::Context context = m_editor->context();
|
|
||||||
context.prepend(BINDINGEDITOR_CONTEXT_ID);
|
|
||||||
m_editorWidget->m_context->setContext(context);
|
|
||||||
|
|
||||||
auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor();
|
|
||||||
|
|
||||||
m_editorWidget->qmljsdocument = qobject_cast<QmlJSEditor::QmlJSEditorWidget *>(
|
|
||||||
qmlDesignerEditor->widget())->qmlJsEditorDocument();
|
|
||||||
|
|
||||||
|
|
||||||
m_editorWidget->setLineNumbersVisible(false);
|
|
||||||
m_editorWidget->setMarksVisible(false);
|
|
||||||
m_editorWidget->setCodeFoldingSupported(false);
|
|
||||||
m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
m_editorWidget->setTabChangesFocus(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingEditorDialog::setupUIComponents()
|
void BindingEditorDialog::setupUIComponents()
|
||||||
{
|
{
|
||||||
m_verticalLayout = new QVBoxLayout(this);
|
|
||||||
|
|
||||||
if (m_dialogType == DialogType::BindingDialog) {
|
|
||||||
m_comboBoxLayout = new QHBoxLayout;
|
|
||||||
m_comboBoxItem = new QComboBox(this);
|
m_comboBoxItem = new QComboBox(this);
|
||||||
m_comboBoxProperty = new QComboBox(this);
|
m_comboBoxProperty = new QComboBox(this);
|
||||||
}
|
|
||||||
|
|
||||||
m_editorWidget->setParent(this);
|
|
||||||
m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
|
||||||
m_editorWidget->show();
|
|
||||||
|
|
||||||
m_buttonBox = new QDialogButtonBox(this);
|
|
||||||
m_buttonBox->setOrientation(Qt::Horizontal);
|
|
||||||
m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
||||||
m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
|
|
||||||
|
|
||||||
if (m_dialogType == DialogType::BindingDialog) {
|
|
||||||
m_comboBoxLayout->addWidget(m_comboBoxItem);
|
m_comboBoxLayout->addWidget(m_comboBoxItem);
|
||||||
m_comboBoxLayout->addWidget(m_comboBoxProperty);
|
m_comboBoxLayout->addWidget(m_comboBoxProperty);
|
||||||
m_verticalLayout->addLayout(m_comboBoxLayout);
|
|
||||||
}
|
|
||||||
m_verticalLayout->addWidget(m_editorWidget);
|
|
||||||
m_verticalLayout->addWidget(m_buttonBox);
|
|
||||||
|
|
||||||
this->resize(660, 240);
|
//this->resize(660, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingEditorDialog::setupComboBoxes()
|
void BindingEditorDialog::setupComboBoxes()
|
||||||
@@ -260,14 +156,4 @@ void BindingEditorDialog::propertyIDChanged(int propertyID)
|
|||||||
m_comboBoxProperty->removeItem(undefinedProperty);
|
m_comboBoxProperty->removeItem(undefinedProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingEditorDialog::textChanged()
|
|
||||||
{
|
|
||||||
if (m_lock)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_lock = true;
|
|
||||||
adjustProperties();
|
|
||||||
m_lock = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // QmlDesigner namespace
|
} // QmlDesigner namespace
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2019 The Qt Company Ltd.
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of Qt Creator.
|
** This file is part of Qt Creator.
|
||||||
@@ -26,21 +26,15 @@
|
|||||||
#ifndef BINDINGEDITORDIALOG_H
|
#ifndef BINDINGEDITORDIALOG_H
|
||||||
#define BINDINGEDITORDIALOG_H
|
#define BINDINGEDITORDIALOG_H
|
||||||
|
|
||||||
#include <bindingeditor/bindingeditorwidget.h>
|
#include <bindingeditor/abstracteditordialog.h>
|
||||||
#include <texteditor/texteditor.h>
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QDialogButtonBox;
|
|
||||||
class QVBoxLayout;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
class BindingEditorDialog : public QDialog
|
class BindingEditorDialog : public AbstractEditorDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -57,52 +51,26 @@ public:
|
|||||||
QStringList properties;
|
QStringList properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DialogType {
|
BindingEditorDialog(QWidget *parent = nullptr);
|
||||||
Unknown = 0,
|
|
||||||
BindingDialog = 1,
|
|
||||||
ActionDialog = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
BindingEditorDialog(QWidget *parent = nullptr, DialogType type = DialogType::BindingDialog);
|
|
||||||
~BindingEditorDialog() override;
|
~BindingEditorDialog() override;
|
||||||
|
|
||||||
void showWidget();
|
void adjustProperties() override;
|
||||||
void showWidget(int x, int y);
|
|
||||||
|
|
||||||
QString editorValue() const;
|
void setAllBindings(QList<BindingOption> bindings);
|
||||||
void setEditorValue(const QString &text);
|
|
||||||
|
|
||||||
void setAllBindings(QList<BindingEditorDialog::BindingOption> bindings);
|
|
||||||
void adjustProperties();
|
|
||||||
|
|
||||||
void unregisterAutoCompletion();
|
|
||||||
|
|
||||||
QString defaultTitle() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupJSEditor();
|
|
||||||
void setupUIComponents();
|
void setupUIComponents();
|
||||||
void setupComboBoxes();
|
void setupComboBoxes();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void itemIDChanged(int);
|
void itemIDChanged(int);
|
||||||
void propertyIDChanged(int);
|
void propertyIDChanged(int);
|
||||||
void textChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DialogType m_dialogType = DialogType::BindingDialog;
|
|
||||||
TextEditor::BaseTextEditor *m_editor = nullptr;
|
|
||||||
BindingEditorWidget *m_editorWidget = nullptr;
|
|
||||||
QVBoxLayout *m_verticalLayout = nullptr;
|
|
||||||
QDialogButtonBox *m_buttonBox = nullptr;
|
|
||||||
QHBoxLayout *m_comboBoxLayout = nullptr;
|
|
||||||
QComboBox *m_comboBoxItem = nullptr;
|
QComboBox *m_comboBoxItem = nullptr;
|
||||||
QComboBox *m_comboBoxProperty = nullptr;
|
QComboBox *m_comboBoxProperty = nullptr;
|
||||||
QList<BindingEditorDialog::BindingOption> m_bindings;
|
|
||||||
bool m_lock = false;
|
QList<BindingOption> m_bindings;
|
||||||
const QString undefinedString = {"[Undefined]"};
|
|
||||||
const QString titleString = {tr("Binding Editor")};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,112 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "connectionvisitor.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
ConnectionVisitor::ConnectionVisitor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::StringLiteral *ast)
|
||||||
|
{
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_StringLiteral,
|
||||||
|
ast->value.toString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::NumericLiteral *ast)
|
||||||
|
{
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_NumericLiteral,
|
||||||
|
QString::number(ast->value)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::TrueLiteral *ast)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ast)
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_TrueLiteral, QString("true")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::FalseLiteral *ast)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ast)
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FalseLiteral, QString("false")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::BinaryExpression *ast)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ast)
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_BinaryExpression,
|
||||||
|
QString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::CallExpression *ast)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ast)
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_CallExpression,
|
||||||
|
QString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::ArgumentList *ast)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ast)
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_ArgumentList,
|
||||||
|
QString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::FunctionExpression *ast)
|
||||||
|
{
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FunctionExpression,
|
||||||
|
ast->name.toString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::FieldMemberExpression *ast)
|
||||||
|
{
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FieldMemberExpression,
|
||||||
|
ast->name.toString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionVisitor::visit(QmlJS::AST::IdentifierExpression *ast)
|
||||||
|
{
|
||||||
|
m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_IdentifierExpression,
|
||||||
|
ast->name.toString()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionVisitor::throwRecursionDepthError()
|
||||||
|
{
|
||||||
|
qWarning("Warning: Hit maximum recursion depth while visiting AST in ConnectionVisitor");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QmlDesigner namespace
|
@@ -0,0 +1,66 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONVISITOR_H
|
||||||
|
#define CONNECTIONVISITOR_H
|
||||||
|
|
||||||
|
#include <qmljs/qmljsdocument.h>
|
||||||
|
#include <qmljs/parser/qmljsastvisitor_p.h>
|
||||||
|
#include <qmljs/parser/qmljsast_p.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class ConnectionVisitor : public QmlJS::AST::Visitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ConnectionVisitor();
|
||||||
|
|
||||||
|
bool visit(QmlJS::AST::StringLiteral *ast) override;
|
||||||
|
bool visit(QmlJS::AST::NumericLiteral *ast) override;
|
||||||
|
bool visit(QmlJS::AST::TrueLiteral *ast) override;
|
||||||
|
bool visit(QmlJS::AST::FalseLiteral *ast) override;
|
||||||
|
|
||||||
|
bool visit(QmlJS::AST::BinaryExpression *ast) override;
|
||||||
|
bool visit(QmlJS::AST::CallExpression *ast) override;
|
||||||
|
|
||||||
|
bool visit(QmlJS::AST::ArgumentList *ast) override;
|
||||||
|
bool visit(QmlJS::AST::FunctionExpression *ast) override; // unused
|
||||||
|
|
||||||
|
bool visit(QmlJS::AST::FieldMemberExpression *ast) override;
|
||||||
|
bool visit(QmlJS::AST::IdentifierExpression *ast) override;
|
||||||
|
|
||||||
|
void throwRecursionDepthError() override;
|
||||||
|
|
||||||
|
const QList<QPair<QmlJS::AST::Node::Kind, QString>> &expression() const {
|
||||||
|
return m_expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QPair<QmlJS::AST::Node::Kind, QString>> m_expression;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //CONNECTIONVISITOR_H
|
@@ -60,7 +60,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
|
|||||||
QFrame(parent),
|
QFrame(parent),
|
||||||
ui(new Ui::ConnectionViewWidget)
|
ui(new Ui::ConnectionViewWidget)
|
||||||
{
|
{
|
||||||
m_connectonEditor = new QmlDesigner::ActionEditor(this);
|
m_connectionEditor = new QmlDesigner::ActionEditor(this);
|
||||||
m_bindingEditor = new QmlDesigner::BindingEditor(this);
|
m_bindingEditor = new QmlDesigner::BindingEditor(this);
|
||||||
m_dynamicEditor = new QmlDesigner::BindingEditor(this);
|
m_dynamicEditor = new QmlDesigner::BindingEditor(this);
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
|
|||||||
|
|
||||||
ConnectionViewWidget::~ConnectionViewWidget()
|
ConnectionViewWidget::~ConnectionViewWidget()
|
||||||
{
|
{
|
||||||
delete m_connectonEditor;
|
delete m_connectionEditor;
|
||||||
delete m_bindingEditor;
|
delete m_bindingEditor;
|
||||||
delete m_dynamicEditor;
|
delete m_dynamicEditor;
|
||||||
delete ui;
|
delete ui;
|
||||||
@@ -161,10 +161,14 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
|
|
||||||
menu.addAction(tr("Open Connection Editor"), [&]() {
|
menu.addAction(tr("Open Connection Editor"), [&]() {
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
m_connectonEditor->showWidget();
|
auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model());
|
||||||
m_connectonEditor->setBindingValue(index.data().toString());
|
ModelNode node = connectionModel->connectionView()->rootModelNode();
|
||||||
m_connectonEditor->setModelIndex(index);
|
m_connectionEditor->showWidget();
|
||||||
m_connectonEditor->updateWindowName();
|
m_connectionEditor->setConnectionValue(index.data().toString());
|
||||||
|
m_connectionEditor->setModelIndex(index);
|
||||||
|
m_connectionEditor->setModelNode(node);
|
||||||
|
m_connectionEditor->prepareConnections();
|
||||||
|
m_connectionEditor->updateWindowName();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -455,29 +459,29 @@ void ConnectionViewWidget::addButtonClicked()
|
|||||||
|
|
||||||
void ConnectionViewWidget::editorForConnection()
|
void ConnectionViewWidget::editorForConnection()
|
||||||
{
|
{
|
||||||
QObject::connect(m_connectonEditor, &QmlDesigner::ActionEditor::accepted,
|
QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::accepted,
|
||||||
[&]() {
|
[&]() {
|
||||||
if (m_connectonEditor->hasModelIndex()) {
|
if (m_connectionEditor->hasModelIndex()) {
|
||||||
ConnectionModel *connectionModel = qobject_cast<ConnectionModel *>(ui->connectionView->model());
|
ConnectionModel *connectionModel = qobject_cast<ConnectionModel *>(ui->connectionView->model());
|
||||||
if (connectionModel->connectionView()->isWidgetEnabled()
|
if (connectionModel->connectionView()->isWidgetEnabled()
|
||||||
&& (connectionModel->rowCount() > m_connectonEditor->modelIndex().row())) {
|
&& (connectionModel->rowCount() > m_connectionEditor->modelIndex().row())) {
|
||||||
connectionModel->connectionView()
|
connectionModel->connectionView()
|
||||||
->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() {
|
->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() {
|
||||||
SignalHandlerProperty signalHandler
|
SignalHandlerProperty signalHandler
|
||||||
= connectionModel->signalHandlerPropertyForRow(
|
= connectionModel->signalHandlerPropertyForRow(
|
||||||
m_connectonEditor->modelIndex().row());
|
m_connectionEditor->modelIndex().row());
|
||||||
signalHandler.setSource(m_connectonEditor->bindingValue());
|
signalHandler.setSource(m_connectionEditor->connectionValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
m_connectonEditor->resetModelIndex();
|
m_connectionEditor->resetModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connectonEditor->hideWidget();
|
m_connectionEditor->hideWidget();
|
||||||
});
|
});
|
||||||
QObject::connect(m_connectonEditor, &QmlDesigner::ActionEditor::rejected,
|
QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::rejected,
|
||||||
[&]() {
|
[&]() {
|
||||||
m_connectonEditor->resetModelIndex();
|
m_connectionEditor->resetModelIndex();
|
||||||
m_connectonEditor->hideWidget();
|
m_connectionEditor->hideWidget();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -107,7 +107,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ConnectionViewWidget *ui;
|
Ui::ConnectionViewWidget *ui;
|
||||||
QmlDesigner::ActionEditor *m_connectonEditor; //editor for connections in connection view
|
QmlDesigner::ActionEditor *m_connectionEditor; //editor for connections in connection view
|
||||||
QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view
|
QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view
|
||||||
QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view
|
QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view
|
||||||
|
|
||||||
|
@@ -693,10 +693,16 @@ Project {
|
|||||||
"bindingeditor/bindingeditor.h",
|
"bindingeditor/bindingeditor.h",
|
||||||
"bindingeditor/actioneditor.cpp",
|
"bindingeditor/actioneditor.cpp",
|
||||||
"bindingeditor/actioneditor.h",
|
"bindingeditor/actioneditor.h",
|
||||||
|
"bindingeditor/abstracteditordialog.cpp",
|
||||||
|
"bindingeditor/abstracteditordialog.h",
|
||||||
|
"bindingeditor/actioneditordialog.cpp",
|
||||||
|
"bindingeditor/actioneditordialog.h",
|
||||||
"bindingeditor/bindingeditordialog.cpp",
|
"bindingeditor/bindingeditordialog.cpp",
|
||||||
"bindingeditor/bindingeditordialog.h",
|
"bindingeditor/bindingeditordialog.h",
|
||||||
"bindingeditor/bindingeditorwidget.cpp",
|
"bindingeditor/bindingeditorwidget.cpp",
|
||||||
"bindingeditor/bindingeditorwidget.h",
|
"bindingeditor/bindingeditorwidget.h",
|
||||||
|
"bindingeditor/connectionvisitor.cpp",
|
||||||
|
"bindingeditor/connectionvisitor.h",
|
||||||
"colortool/colortool.cpp",
|
"colortool/colortool.cpp",
|
||||||
"colortool/colortool.h",
|
"colortool/colortool.h",
|
||||||
"connectioneditor/addnewbackenddialog.h",
|
"connectioneditor/addnewbackenddialog.h",
|
||||||
|
Reference in New Issue
Block a user