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.text = backendValue.expression
bindingEditor.prepareBindings()
bindingEditor.updateWindowName()
}
BindingEditor {

View File

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

View File

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

View File

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

View File

@@ -286,10 +286,13 @@ void ActionEditor::prepareConnections()
m_dialog->setAllConnections(connections, singletons, states);
}
void ActionEditor::updateWindowName()
void ActionEditor::updateWindowName(const QString &targetName)
{
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();
}
}

View File

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

View File

@@ -42,8 +42,6 @@
namespace QmlDesigner {
static BindingEditor *s_lastBindingEditor = nullptr;
BindingEditor::BindingEditor(QObject *)
{
}
@@ -62,11 +60,6 @@ void BindingEditor::prepareDialog()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED);
if (s_lastBindingEditor)
s_lastBindingEditor->hideWidget();
s_lastBindingEditor = this;
m_dialog = new BindingEditorDialog(Core::ICore::dialogParent());
QObject::connect(m_dialog, &AbstractEditorDialog::accepted,
@@ -91,9 +84,6 @@ void BindingEditor::showWidget(int x, int y)
void BindingEditor::hideWidget()
{
if (s_lastBindingEditor == this)
s_lastBindingEditor = nullptr;
if (m_dialog) {
m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override
m_dialog->close();
@@ -125,6 +115,12 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
if (node.isValid()) {
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 (QmlObjectNode::isValidQmlObjectNode(node))
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)
{
if (modelNode.isValid())
@@ -177,6 +179,11 @@ void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName
emit backendValueChanged();
}
void BindingEditor::setTargetName(const QString &target)
{
m_targetName = target;
}
void BindingEditor::prepareBindings()
{
if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty())
@@ -279,8 +286,13 @@ void BindingEditor::prepareBindings()
void BindingEditor::updateWindowName()
{
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty())
m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]");
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) {
const QString targetString = " ["
+ (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
+ QString::fromUtf8(m_backendValueTypeName) + "]";
m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
}
}
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 modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged)
Q_PROPERTY(QVariant stateModelNodeProperty READ stateModelNode WRITE setStateModelNode NOTIFY stateModelNodeChanged)
Q_PROPERTY(QString stateNameProperty WRITE setStateName)
public:
BindingEditor(QObject *parent = nullptr);
@@ -64,11 +65,14 @@ public:
void setModelNodeBackend(const QVariant &modelNodeBackend);
//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 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 setBackendValueTypeName(const TypeName &backendValueTypeName);
void setTargetName(const QString &target);
Q_INVOKABLE void prepareBindings();
Q_INVOKABLE void updateWindowName();
@@ -93,6 +97,7 @@ private:
QVariant m_stateModelNode;
QmlDesigner::ModelNode m_modelNode;
TypeName m_backendValueTypeName;
QString m_targetName;
};
}

View File

@@ -35,6 +35,10 @@
#include <qmljseditor/qmljseditordocument.h>
#include <qmljseditor/qmljssemantichighlighter.h>
#include <qmljstools/qmljsindenter.h>
#include <qmljstools/qmljstoolsconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/fancylineedit.h>
#include <QAction>
@@ -43,17 +47,19 @@ namespace QmlDesigner {
BindingEditorWidget::BindingEditorWidget()
: m_context(new Core::IContext(this))
{
m_context->setWidget(this);
Core::ICore::addContextObject(m_context);
Core::Context context(BINDINGEDITOR_CONTEXT_ID,
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
* use the cursor position of the original editor in the editor manager.
*/
m_completionAction = new QAction(tr("Trigger Completion"), this);
Core::Command *command = Core::ActionManager::registerAction(
m_completionAction, TextEditor::Constants::COMPLETE_THIS, context);
command->setDefaultKeySequence(QKeySequence(
@@ -84,11 +90,9 @@ bool BindingEditorWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
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();
return true;
} else {
return QmlJSEditor::QmlJSEditorWidget::event(event);
}
}
return QmlJSEditor::QmlJSEditorWidget::event(event);
@@ -133,8 +137,12 @@ void BindingDocument::triggerPendingUpdates()
BindingEditorFactory::BindingEditorFactory()
{
setId(BINDINGEDITOR_CONTEXT_ID);
setDisplayName(QCoreApplication::translate("OpenWith::Editors", QmlDesigner::BINDINGEDITOR_CONTEXT_ID));
setDisplayName(QCoreApplication::translate("OpenWith::Editors", BINDINGEDITOR_CONTEXT_ID));
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; });
setEditorWidgetCreator([]() { return new BindingEditorWidget; });

View File

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