QmlDesigner: Fix for Binding Editor

- Makes Binding Editor modal
 - Fixes Binding Editors hotkeys and actions
 - Adds Target item, property and expected type into title

Task-numbers: QDS-2819, QDS-4878
Change-Id: Ib5c5f73e6552f58828776043f9b793a24c48a1f8
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Aleksei German
2021-09-10 18:06:26 +02:00
parent 786dbcb9e8
commit 3ccf269f81
10 changed files with 76 additions and 36 deletions

View File

@@ -164,6 +164,7 @@ Item {
bindingEditor.showWidget() bindingEditor.showWidget()
bindingEditor.text = backendValue.expression bindingEditor.text = backendValue.expression
bindingEditor.prepareBindings() bindingEditor.prepareBindings()
bindingEditor.updateWindowName()
} }
BindingEditor { BindingEditor {

View File

@@ -110,6 +110,7 @@ Rectangle {
bindingEditor.showWidget() bindingEditor.showWidget()
bindingEditor.text = delegateWhenConditionString bindingEditor.text = delegateWhenConditionString
bindingEditor.prepareBindings() bindingEditor.prepareBindings()
bindingEditor.updateWindowName()
} }
} }
@@ -309,6 +310,7 @@ Rectangle {
} }
stateModelNodeProperty: statesEditorModel.stateModelNode() stateModelNodeProperty: statesEditorModel.stateModelNode()
stateNameProperty: myRoot.delegateStateName
onRejected: { onRejected: {
hideWidget() hideWidget()

View File

@@ -46,7 +46,7 @@ AbstractEditorDialog::AbstractEditorDialog(QWidget *parent, const QString &title
{ {
setWindowFlag(Qt::Tool, true); setWindowFlag(Qt::Tool, true);
setWindowTitle(defaultTitle()); setWindowTitle(defaultTitle());
setModal(false); setModal(true);
setupJSEditor(); setupJSEditor();
setupUIComponents(); setupUIComponents();
@@ -111,11 +111,10 @@ void AbstractEditorDialog::setupJSEditor()
{ {
static BindingEditorFactory f; static BindingEditorFactory f;
m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor()); m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor());
m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget()); Q_ASSERT(m_editor);
Core::Context context = m_editor->context(); m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget());
context.prepend(BINDINGEDITOR_CONTEXT_ID); Q_ASSERT(m_editorWidget);
m_editorWidget->m_context->setContext(context);
auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor();

View File

@@ -26,9 +26,10 @@
#ifndef ABSTRACTEDITORDIALOG_H #ifndef ABSTRACTEDITORDIALOG_H
#define ABSTRACTEDITORDIALOG_H #define ABSTRACTEDITORDIALOG_H
#include <bindingeditor/bindingeditorwidget.h>
#include <qmldesignercorelib_global.h> #include <qmldesignercorelib_global.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <bindingeditor/bindingeditorwidget.h>
#include <QDialog> #include <QDialog>

View File

@@ -286,10 +286,13 @@ void ActionEditor::prepareConnections()
m_dialog->setAllConnections(connections, singletons, states); m_dialog->setAllConnections(connections, singletons, states);
} }
void ActionEditor::updateWindowName() void ActionEditor::updateWindowName(const QString &targetName)
{ {
if (!m_dialog.isNull()) { if (!m_dialog.isNull()) {
m_dialog->setWindowTitle(m_dialog->defaultTitle()); if (targetName.isEmpty())
m_dialog->setWindowTitle(m_dialog->defaultTitle());
else
m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + targetName + "]");
m_dialog->raise(); m_dialog->raise();
} }
} }

View File

@@ -64,7 +64,7 @@ public:
void prepareConnections(); void prepareConnections();
Q_INVOKABLE void updateWindowName(); Q_INVOKABLE void updateWindowName(const QString &targetName = {});
signals: signals:
void accepted(); void accepted();

View File

@@ -42,8 +42,6 @@
namespace QmlDesigner { namespace QmlDesigner {
static BindingEditor *s_lastBindingEditor = nullptr;
BindingEditor::BindingEditor(QObject *) BindingEditor::BindingEditor(QObject *)
{ {
} }
@@ -62,11 +60,6 @@ void BindingEditor::prepareDialog()
{ {
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED); QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED);
if (s_lastBindingEditor)
s_lastBindingEditor->hideWidget();
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, &AbstractEditorDialog::accepted,
@@ -91,9 +84,6 @@ void BindingEditor::showWidget(int x, int y)
void BindingEditor::hideWidget() void BindingEditor::hideWidget()
{ {
if (s_lastBindingEditor == this)
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();
@@ -125,6 +115,12 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
if (node.isValid()) { if (node.isValid()) {
m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name()); m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name());
QString nodeId = node.id();
if (nodeId.isEmpty())
nodeId = node.simplifiedTypeName();
m_targetName = nodeId + "." + propertyEditorValue->name();
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown") if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown")
if (QmlObjectNode::isValidQmlObjectNode(node)) if (QmlObjectNode::isValidQmlObjectNode(node))
m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name()); m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name());
@@ -164,6 +160,12 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
} }
} }
void BindingEditor::setStateName(const QString &name)
{
m_targetName = name;
m_targetName += ".when";
}
void BindingEditor::setModelNode(const ModelNode &modelNode) void BindingEditor::setModelNode(const ModelNode &modelNode)
{ {
if (modelNode.isValid()) if (modelNode.isValid())
@@ -177,6 +179,11 @@ void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName
emit backendValueChanged(); emit backendValueChanged();
} }
void BindingEditor::setTargetName(const QString &target)
{
m_targetName = target;
}
void BindingEditor::prepareBindings() void BindingEditor::prepareBindings()
{ {
if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty()) if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty())
@@ -279,8 +286,13 @@ 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 + "]"); const QString targetString = " ["
+ (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
+ QString::fromUtf8(m_backendValueTypeName) + "]";
m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
}
} }
QVariant BindingEditor::backendValue() const QVariant BindingEditor::backendValue() const

View File

@@ -44,6 +44,7 @@ class BindingEditor : public QObject
Q_PROPERTY(QVariant backendValueProperty READ backendValue WRITE setBackendValue NOTIFY backendValueChanged) Q_PROPERTY(QVariant backendValueProperty READ backendValue WRITE setBackendValue NOTIFY backendValueChanged)
Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged)
Q_PROPERTY(QVariant stateModelNodeProperty READ stateModelNode WRITE setStateModelNode NOTIFY stateModelNodeChanged) Q_PROPERTY(QVariant stateModelNodeProperty READ stateModelNode WRITE setStateModelNode NOTIFY stateModelNodeChanged)
Q_PROPERTY(QString stateNameProperty WRITE setStateName)
public: public:
BindingEditor(QObject *parent = nullptr); BindingEditor(QObject *parent = nullptr);
@@ -64,11 +65,14 @@ public:
void setModelNodeBackend(const QVariant &modelNodeBackend); void setModelNodeBackend(const QVariant &modelNodeBackend);
//2. modelnode (this one also sets backend value type name to bool) //2. modelnode (this one also sets backend value type name to bool)
//State Name is not mandatory, but used in bindingEditor dialog name
void setStateModelNode(const QVariant &stateModelNode); void setStateModelNode(const QVariant &stateModelNode);
void setStateName(const QString &name);
//3. modelnode + backend value type name //3. modelnode + backend value type name + optional target name
void setModelNode(const ModelNode &modelNode); void setModelNode(const ModelNode &modelNode);
void setBackendValueTypeName(const TypeName &backendValueTypeName); void setBackendValueTypeName(const TypeName &backendValueTypeName);
void setTargetName(const QString &target);
Q_INVOKABLE void prepareBindings(); Q_INVOKABLE void prepareBindings();
Q_INVOKABLE void updateWindowName(); Q_INVOKABLE void updateWindowName();
@@ -93,6 +97,7 @@ private:
QVariant m_stateModelNode; QVariant m_stateModelNode;
QmlDesigner::ModelNode m_modelNode; QmlDesigner::ModelNode m_modelNode;
TypeName m_backendValueTypeName; TypeName m_backendValueTypeName;
QString m_targetName;
}; };
} }

View File

@@ -35,6 +35,10 @@
#include <qmljseditor/qmljseditordocument.h> #include <qmljseditor/qmljseditordocument.h>
#include <qmljseditor/qmljssemantichighlighter.h> #include <qmljseditor/qmljssemantichighlighter.h>
#include <qmljstools/qmljsindenter.h> #include <qmljstools/qmljsindenter.h>
#include <qmljstools/qmljstoolsconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/fancylineedit.h>
#include <QAction> #include <QAction>
@@ -43,17 +47,19 @@ namespace QmlDesigner {
BindingEditorWidget::BindingEditorWidget() BindingEditorWidget::BindingEditorWidget()
: m_context(new Core::IContext(this)) : m_context(new Core::IContext(this))
{ {
m_context->setWidget(this); Core::Context context(BINDINGEDITOR_CONTEXT_ID,
Core::ICore::addContextObject(m_context); ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
const Core::Context context(BINDINGEDITOR_CONTEXT_ID); m_context->setWidget(this);
m_context->setContext(context);
Core::ICore::addContextObject(m_context);
/* /*
* We have to register our own active auto completion shortcut, because the original short cut will * We have to register our own active auto completion shortcut, because the original short cut will
* use the cursor position of the original editor in the editor manager. * use the cursor position of the original editor in the editor manager.
*/ */
m_completionAction = new QAction(tr("Trigger Completion"), this); m_completionAction = new QAction(tr("Trigger Completion"), this);
Core::Command *command = Core::ActionManager::registerAction( Core::Command *command = Core::ActionManager::registerAction(
m_completionAction, TextEditor::Constants::COMPLETE_THIS, context); m_completionAction, TextEditor::Constants::COMPLETE_THIS, context);
command->setDefaultKeySequence(QKeySequence( command->setDefaultKeySequence(QKeySequence(
@@ -84,11 +90,9 @@ bool BindingEditorWidget::event(QEvent *event)
{ {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && !keyEvent->modifiers()) {
emit returnKeyClicked(); emit returnKeyClicked();
return true; return true;
} else {
return QmlJSEditor::QmlJSEditorWidget::event(event);
} }
} }
return QmlJSEditor::QmlJSEditorWidget::event(event); return QmlJSEditor::QmlJSEditorWidget::event(event);
@@ -133,8 +137,12 @@ void BindingDocument::triggerPendingUpdates()
BindingEditorFactory::BindingEditorFactory() BindingEditorFactory::BindingEditorFactory()
{ {
setId(BINDINGEDITOR_CONTEXT_ID); setId(BINDINGEDITOR_CONTEXT_ID);
setDisplayName(QCoreApplication::translate("OpenWith::Editors", QmlDesigner::BINDINGEDITOR_CONTEXT_ID)); setDisplayName(QCoreApplication::translate("OpenWith::Editors", BINDINGEDITOR_CONTEXT_ID));
setEditorActionHandlers(0); setEditorActionHandlers(0);
addMimeType(BINDINGEDITOR_CONTEXT_ID);
addMimeType(QmlJSTools::Constants::QML_MIMETYPE);
addMimeType(QmlJSTools::Constants::QMLTYPES_MIMETYPE);
addMimeType(QmlJSTools::Constants::JS_MIMETYPE);
setDocumentCreator([]() { return new BindingDocument; }); setDocumentCreator([]() { return new BindingDocument; });
setEditorWidgetCreator([]() { return new BindingEditorWidget; }); setEditorWidgetCreator([]() { return new BindingEditorWidget; });

View File

@@ -159,17 +159,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
QMenu menu(this); QMenu menu(this);
menu.addAction(tr("Open Connection Editor"), [&]() { menu.addAction(tr("Open Connection Editor"), this, [&]() {
auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model()); auto *connectionModel = qobject_cast<ConnectionModel *>(targetView->model());
const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row()); const SignalHandlerProperty property = connectionModel->signalHandlerPropertyForRow(index.row());
const ModelNode node = property.parentModelNode(); const ModelNode node = property.parentModelNode();
const QString targetName = index.siblingAtColumn(ConnectionModel::TargetModelNodeRow).data().toString()
+ "." + property.name();
m_connectionEditor->showWidget(); m_connectionEditor->showWidget();
m_connectionEditor->setConnectionValue(index.data().toString()); m_connectionEditor->setConnectionValue(index.data().toString());
m_connectionEditor->setModelIndex(index); m_connectionEditor->setModelIndex(index);
m_connectionEditor->setModelNode(node); m_connectionEditor->setModelNode(node);
m_connectionEditor->prepareConnections(); m_connectionEditor->prepareConnections();
m_connectionEditor->updateWindowName(); m_connectionEditor->updateWindowName(targetName);
}); });
QMap<QString, QVariant> data; QMap<QString, QVariant> data;
@@ -179,7 +182,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
const auto actions = designerActionManager.actionsForTargetView( const auto actions = designerActionManager.actionsForTargetView(
ActionInterface::TargetView::ConnectionEditor); ActionInterface::TargetView::ConnectionEditor);
for (auto actionInterface : actions) { for (const auto &actionInterface : actions) {
auto *action = actionInterface->action(); auto *action = actionInterface->action();
action->setData(data); action->setData(data);
menu.addAction(action); menu.addAction(action);
@@ -198,7 +201,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
QMenu menu(this); QMenu menu(this);
menu.addAction(tr("Open Binding Editor"), [&]() { menu.addAction(tr("Open Binding Editor"), this, [&]() {
BindingModel *bindingModel = qobject_cast<BindingModel*>(targetView->model()); BindingModel *bindingModel = qobject_cast<BindingModel*>(targetView->model());
const BindingProperty property = bindingModel->bindingPropertyForRow(index.row()); const BindingProperty property = bindingModel->bindingPropertyForRow(index.row());
@@ -209,10 +212,13 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
const TypeName typeName = property.isDynamic() ? property.dynamicTypeName() const TypeName typeName = property.isDynamic() ? property.dynamicTypeName()
: node.metaInfo().propertyTypeName(property.name()); : node.metaInfo().propertyTypeName(property.name());
const QString targetName = node.displayName() + "." + property.name();
m_bindingEditor->showWidget(); m_bindingEditor->showWidget();
m_bindingEditor->setBindingValue(property.expression()); m_bindingEditor->setBindingValue(property.expression());
m_bindingEditor->setModelNode(node); m_bindingEditor->setModelNode(node);
m_bindingEditor->setBackendValueTypeName(typeName); m_bindingEditor->setBackendValueTypeName(typeName);
m_bindingEditor->setTargetName(targetName);
m_bindingEditor->prepareBindings(); m_bindingEditor->prepareBindings();
m_bindingEditor->updateWindowName(); m_bindingEditor->updateWindowName();
@@ -232,7 +238,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(targetView->model()); DynamicPropertiesModel *propertiesModel = qobject_cast<DynamicPropertiesModel *>(targetView->model());
QMenu menu(this); QMenu menu(this);
menu.addAction(tr("Open Binding Editor"), [&]() { menu.addAction(tr("Open Binding Editor"), this, [&]() {
AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row()); AbstractProperty abstractProperty = propertiesModel->abstractPropertyForRow(index.row());
if (!abstractProperty.isValid()) if (!abstractProperty.isValid())
return; return;
@@ -247,17 +253,20 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
else else
return; return;
const QString targetName = node.displayName() + "." + abstractProperty.name();
m_dynamicEditor->showWidget(); m_dynamicEditor->showWidget();
m_dynamicEditor->setBindingValue(newExpression); m_dynamicEditor->setBindingValue(newExpression);
m_dynamicEditor->setModelNode(node); m_dynamicEditor->setModelNode(node);
m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName()); m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName());
m_dynamicEditor->setTargetName(targetName);
m_dynamicEditor->prepareBindings(); m_dynamicEditor->prepareBindings();
m_dynamicEditor->updateWindowName(); m_dynamicEditor->updateWindowName();
m_dynamicIndex = index; m_dynamicIndex = index;
}); });
menu.addAction(tr("Reset Property"), [&]() { menu.addAction(tr("Reset Property"), this, [&]() {
propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name()); propertiesModel->resetProperty(propertiesModel->abstractPropertyForRow(index.row()).name());
}); });