From 0adf810587ad97633c4e01a782529aeeb3bd57ac Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 5 Dec 2019 14:33:42 +0100 Subject: QmlDesigner Action Editor for Connections View Task: QDS-1261 Change-Id: I81e6687e31a0f987ba15dc81dd52f240e4ea9689 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/bindingeditor/actioneditor.cpp | 131 +++++++++++++++++++++ .../components/bindingeditor/actioneditor.h | 82 +++++++++++++ .../components/bindingeditor/bindingeditor.pri | 2 + .../bindingeditor/bindingeditordialog.cpp | 35 +++--- .../components/bindingeditor/bindingeditordialog.h | 9 +- .../connectioneditor/connectionmodel.cpp | 9 +- .../components/connectioneditor/connectionmodel.h | 2 + .../components/connectioneditor/connectionview.cpp | 8 ++ .../components/connectioneditor/connectionview.h | 1 + .../connectioneditor/connectionviewwidget.cpp | 54 +++++++++ .../connectioneditor/connectionviewwidget.h | 6 + .../propertyeditor/quick2propertyeditorview.cpp | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 14 files changed, 327 insertions(+), 17 deletions(-) create mode 100644 src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp create mode 100644 src/plugins/qmldesigner/components/bindingeditor/actioneditor.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 80a03f448c..1685aeaa79 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -526,6 +526,7 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/bindingeditor SOURCES bindingeditor.cpp bindingeditor.h + actioneditor.cpp actioneditor.h bindingeditordialog.cpp bindingeditordialog.h bindingeditorwidget.cpp bindingeditorwidget.h ) diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp new file mode 100644 index 0000000000..e21000a171 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "actioneditor.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +static ActionEditor *s_lastActionEditor = nullptr; + +ActionEditor::ActionEditor(QObject *) + : m_index(QModelIndex()) +{ +} + +ActionEditor::~ActionEditor() +{ + hideWidget(); +} + +void ActionEditor::registerDeclarativeType() +{ + qmlRegisterType("HelperWidgets", 2, 0, "ActionEditor"); +} + +void ActionEditor::showWidget(int x, int y) +{ + if (s_lastActionEditor) + s_lastActionEditor->hideWidget(); + s_lastActionEditor = this; + + m_dialog = new BindingEditorDialog(Core::ICore::dialogParent(), + BindingEditorDialog::DialogType::ActionDialog); + + + QObject::connect(m_dialog, &BindingEditorDialog::accepted, + this, &ActionEditor::accepted); + QObject::connect(m_dialog, &BindingEditorDialog::rejected, + this, &ActionEditor::rejected); + + m_dialog->setAttribute(Qt::WA_DeleteOnClose); + m_dialog->showWidget(x, y); + m_dialog->activateWindow(); +} + +void ActionEditor::hideWidget() +{ + if (s_lastActionEditor == this) + s_lastActionEditor = nullptr; + if (m_dialog) + { + m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override + m_dialog->close(); + } +} + +QString ActionEditor::bindingValue() const +{ + if (!m_dialog) + return {}; + + return m_dialog->editorValue(); +} + +void ActionEditor::setBindingValue(const QString &text) +{ + if (m_dialog) + m_dialog->setEditorValue(text); +} + +bool ActionEditor::hasModelIndex() const +{ + return m_index.isValid(); +} + +void ActionEditor::resetModelIndex() +{ + m_index = QModelIndex(); +} + +QModelIndex ActionEditor::modelIndex() const +{ + return m_index; +} + +void ActionEditor::setModelIndex(const QModelIndex &index) +{ + m_index = index; +} + +void ActionEditor::updateWindowName() +{ + if (!m_dialog.isNull()) + { + m_dialog->setWindowTitle(tr("Action Editor")); + m_dialog->raise(); + } +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h new file mode 100644 index 0000000000..b8133224fc --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 ACTIONEDITOR_H +#define ACTIONEDITOR_H + +#include +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +class ActionEditor : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString text READ bindingValue WRITE setBindingValue) + +public: + ActionEditor(QObject *parent = nullptr); + ~ActionEditor(); + + static void registerDeclarativeType(); + + Q_INVOKABLE void showWidget(int x, int y); + Q_INVOKABLE void hideWidget(); + + QString bindingValue() const; + void setBindingValue(const QString &text); + + bool hasModelIndex() const; + void resetModelIndex(); + QModelIndex modelIndex() const; + void setModelIndex(const QModelIndex &index); + + Q_INVOKABLE void updateWindowName(); + +signals: + void accepted(); + void rejected(); + +private: + QVariant backendValue() const; + QVariant modelNodeBackend() const; + QVariant stateModelNode() const; + +private: + QPointer m_dialog; + QModelIndex m_index; +}; + +} + +QML_DECLARE_TYPE(QmlDesigner::ActionEditor) + +#endif //ACTIONEDITOR_H diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri index 88b2897c7b..925cea3539 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri @@ -1,7 +1,9 @@ HEADERS += $$PWD/bindingeditor.h +HEADERS += $$PWD/actioneditor.h HEADERS += $$PWD/bindingeditordialog.h HEADERS += $$PWD/bindingeditorwidget.h SOURCES += $$PWD/bindingeditor.cpp +SOURCES += $$PWD/actioneditor.cpp SOURCES += $$PWD/bindingeditordialog.cpp SOURCES += $$PWD/bindingeditorwidget.cpp diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp index 228020d238..f368aee38b 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp @@ -41,8 +41,9 @@ namespace QmlDesigner { -BindingEditorDialog::BindingEditorDialog(QWidget *parent) +BindingEditorDialog::BindingEditorDialog(QWidget *parent, DialogType type) : QDialog(parent) + , m_dialogType(type) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlag(Qt::Tool, true); @@ -59,12 +60,14 @@ BindingEditorDialog::BindingEditorDialog(QWidget *parent) QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked, this, &BindingEditorDialog::accepted); - QObject::connect(m_comboBoxItem, QOverload::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::itemIDChanged); - QObject::connect(m_comboBoxProperty, QOverload::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::propertyIDChanged); - QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, - this, &BindingEditorDialog::textChanged); + if (m_dialogType == DialogType::BindingDialog) { + QObject::connect(m_comboBoxItem, QOverload::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::itemIDChanged); + QObject::connect(m_comboBoxProperty, QOverload::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::propertyIDChanged); + QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, + this, &BindingEditorDialog::textChanged); + } } BindingEditorDialog::~BindingEditorDialog() @@ -186,10 +189,12 @@ void BindingEditorDialog::setupJSEditor() void BindingEditorDialog::setupUIComponents() { m_verticalLayout = new QVBoxLayout(this); - m_comboBoxLayout = new QHBoxLayout; - m_comboBoxItem = new QComboBox(this); - m_comboBoxProperty = new QComboBox(this); + if (m_dialogType == DialogType::BindingDialog) { + m_comboBoxLayout = new QHBoxLayout; + m_comboBoxItem = new QComboBox(this); + m_comboBoxProperty = new QComboBox(this); + } m_editorWidget->setParent(this); m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); @@ -200,11 +205,11 @@ void BindingEditorDialog::setupUIComponents() m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - - m_comboBoxLayout->addWidget(m_comboBoxItem); - m_comboBoxLayout->addWidget(m_comboBoxProperty); - - m_verticalLayout->addLayout(m_comboBoxLayout); + if (m_dialogType == DialogType::BindingDialog) { + m_comboBoxLayout->addWidget(m_comboBoxItem); + m_comboBoxLayout->addWidget(m_comboBoxProperty); + m_verticalLayout->addLayout(m_comboBoxLayout); + } m_verticalLayout->addWidget(m_editorWidget); m_verticalLayout->addWidget(m_buttonBox); diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h index 6cb0c00361..7255d528e6 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h @@ -57,8 +57,14 @@ public: QStringList properties; }; + enum DialogType { + Unknown = 0, + BindingDialog = 1, + ActionDialog = 2 + }; + public: - BindingEditorDialog(QWidget *parent = nullptr); + BindingEditorDialog(QWidget *parent = nullptr, DialogType type = DialogType::BindingDialog); ~BindingEditorDialog() override; void showWidget(int x, int y); @@ -84,6 +90,7 @@ public slots: void textChanged(); private: + DialogType m_dialogType = DialogType::BindingDialog; TextEditor::BaseTextEditor *m_editor = nullptr; BindingEditorWidget *m_editorWidget = nullptr; QVBoxLayout *m_verticalLayout = nullptr; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 80caf51ce0..87de1e5074 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -47,6 +47,7 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr foreach (QmlDesigner::PropertyName propertyName, propertyNameList) { stringList << QString::fromUtf8(propertyName); } + stringList.removeDuplicates(); return stringList; } @@ -127,7 +128,7 @@ void ConnectionModel::addSignalHandler(const SignalHandlerProperty &signalHandle ModelNode connectionsModelNode = signalHandlerProperty.parentModelNode(); if (connectionsModelNode.bindingProperty("target").isValid()) { - idLabel =connectionsModelNode.bindingProperty("target").expression(); + idLabel = connectionsModelNode.bindingProperty("target").expression(); } targetItem = new QStandardItem(idLabel); @@ -281,6 +282,12 @@ void ConnectionModel::variantPropertyChanged(const VariantProperty &variantPrope resetModel(); } +void ConnectionModel::abstractPropertyChanged(const AbstractProperty &abstractProperty) +{ + if (isConnection(abstractProperty.parentModelNode())) + resetModel(); +} + void ConnectionModel::deleteConnectionByRow(int currentRow) { signalHandlerPropertyForRow(currentRow).parentModelNode().destroy(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 2c66b2ef25..1ae3aa2a25 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -29,6 +29,7 @@ namespace QmlDesigner { +class AbstractProperty; class ModelNode; class BindingProperty; class SignalHandlerProperty; @@ -59,6 +60,7 @@ public: void bindingPropertyChanged(const BindingProperty &bindingProperty); void variantPropertyChanged(const VariantProperty &variantProperty); + void abstractPropertyChanged(const AbstractProperty &abstractProperty); void deleteConnectionByRow(int currentRow); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 9aef01259e..08be13d7d5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace QmlDesigner { @@ -139,6 +140,13 @@ void ConnectionView::bindingPropertiesChanged(const QList &prop } } +void ConnectionView::signalHandlerPropertiesChanged(const QVector &propertyList, + AbstractView::PropertyChangeFlags /*propertyChange*/) +{ + for (const SignalHandlerProperty &signalHandlerProperty : propertyList) + connectionModel()->abstractPropertyChanged(signalHandlerProperty); +} + void ConnectionView::selectedNodesChanged(const QList & selectedNodeList, const QList & /*lastSelectedNodeList*/) { diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index da7623375a..09b653072e 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -65,6 +65,7 @@ public: void propertiesAboutToBeRemoved(const QList& propertyList) override; void variantPropertiesChanged(const QList& propertyList, PropertyChangeFlags propertyChange) override; void bindingPropertiesChanged(const QList& propertyList, PropertyChangeFlags propertyChange) override; + void signalHandlerPropertiesChanged(const QVector& propertyList, PropertyChangeFlags propertyChange) override; void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index c9b1277ce4..1d9d7c362e 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -33,6 +33,7 @@ #include "connectionmodel.h" #include "dynamicpropertiesmodel.h" #include "theme.h" +#include "signalhandlerproperty.h" #include #include @@ -43,6 +44,9 @@ #include #include +#include + +#include namespace QmlDesigner { @@ -52,6 +56,27 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : QFrame(parent), ui(new Ui::ConnectionViewWidget) { + m_actionEditor = new QmlDesigner::ActionEditor(this); + QObject::connect(m_actionEditor, &QmlDesigner::ActionEditor::accepted, + [&]() { + if (m_actionEditor->hasModelIndex()) { + ConnectionModel *connectionModel = qobject_cast(ui->connectionView->model()); + if (connectionModel->rowCount() > m_actionEditor->modelIndex().row()) + { + SignalHandlerProperty signalHandler = + connectionModel->signalHandlerPropertyForRow(m_actionEditor->modelIndex().row()); + signalHandler.setSource(m_actionEditor->bindingValue()); + } + m_actionEditor->resetModelIndex(); + } + + m_actionEditor->hideWidget(); + }); + QObject::connect(m_actionEditor, &QmlDesigner::ActionEditor::rejected, + [&]() { + m_actionEditor->resetModelIndex(); + m_actionEditor->hideWidget(); + }); setWindowTitle(tr("Connections", "Title of connection view")); ui->setupUi(this); @@ -96,6 +121,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : ConnectionViewWidget::~ConnectionViewWidget() { + delete m_actionEditor; delete ui; } @@ -116,9 +142,37 @@ void ConnectionViewWidget::setConnectionModel(ConnectionModel *model) ui->connectionView->horizontalHeader()->setDefaultSectionSize(160); ui->connectionView->setSelectionMode(QAbstractItemView::SingleSelection); ui->connectionView->setItemDelegate(new ConnectionDelegate); + connect(ui->connectionView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &ConnectionViewWidget::connectionTableViewSelectionChanged); +} + +void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) +{ + if (currentTab() != ConnectionTab || ui->connectionView == nullptr) + return; + + //adjusting qpoint to the qtableview entrances: + QPoint posInTable(ui->connectionView->mapFromGlobal(mapToGlobal(event->pos()))); + posInTable = QPoint(posInTable.x(), posInTable.y() - ui->connectionView->horizontalHeader()->height()); + + //making sure that we have source column in our hands: + QModelIndex index = ui->connectionView->indexAt(posInTable).siblingAtColumn(ConnectionModel::SourceRow); + if (!index.isValid()) + return; + + QMenu menu(this); + + menu.addAction(tr("Open Action Editor"), [&]() { + if (index.isValid()) { + m_actionEditor->showWidget(mapToGlobal(event->pos()).x(), mapToGlobal(event->pos()).y()); + m_actionEditor->setBindingValue(index.data().toString()); + m_actionEditor->setModelIndex(index); + m_actionEditor->updateWindowName(); + } + }); + menu.exec(event->globalPos()); } void ConnectionViewWidget::setDynamicPropertiesModel(DynamicPropertiesModel *model) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h index 2bcc3932c1..09cc4f6c53 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h @@ -38,6 +38,8 @@ namespace QmlDesigner { namespace Ui { class ConnectionViewWidget; } +class ActionEditor; + namespace Internal { class BindingModel; @@ -88,6 +90,9 @@ signals: void setEnabledAddButton(bool enabled); void setEnabledRemoveButton(bool enabled); +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + private: void handleTabChanged(int i); void removeButtonClicked(); @@ -95,6 +100,7 @@ private: private: Ui::ConnectionViewWidget *ui; + QmlDesigner::ActionEditor *m_actionEditor; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 3987435802..096162490e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -33,6 +33,7 @@ #include "itemfiltermodel.h" #include "simplecolorpalettemodel.h" #include "bindingeditor/bindingeditor.h" +#include "bindingeditor/actioneditor.h" #include "qmlanchorbindingproxy.h" #include "theme.h" #include "aligndistribute.h" @@ -60,6 +61,7 @@ void Quick2PropertyEditorView::registerQmlTypes() SimpleColorPaletteModel::registerDeclarativeType(); Internal::QmlAnchorBindingProxy::registerDeclarativeType(); BindingEditor::registerDeclarativeType(); + ActionEditor::registerDeclarativeType(); AlignDistribute::registerDeclarativeType(); } } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 466ff28a7a..7047ffa1b8 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -634,6 +634,8 @@ Project { files: [ "bindingeditor/bindingeditor.cpp", "bindingeditor/bindingeditor.h", + "bindingeditor/actioneditor.cpp", + "bindingeditor/actioneditor.h", "bindingeditor/bindingeditordialog.cpp", "bindingeditor/bindingeditordialog.h", "bindingeditor/bindingeditorwidget.cpp", -- cgit v1.2.1