diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 89ddbed9d9d..0d07029bf9c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -497,6 +497,7 @@ extend_qtc_plugin(QmlDesigner include/qmlmodelnodefacade.h include/qmlobjectnode.h include/qmlstate.h + include/qmlconnections.h include/qmltimeline.h include/qmltimelinekeyframegroup.h include/removebasestateexception.h @@ -578,6 +579,7 @@ extend_qtc_plugin(QmlDesigner model/qmlmodelnodefacade.cpp model/qmlobjectnode.cpp model/qmlstate.cpp + model/qmlconnections.cpp model/qmltextgenerator.cpp model/qmltextgenerator.h model/qmltimeline.cpp model/qmltimelinekeyframegroup.cpp @@ -620,6 +622,9 @@ extend_qtc_plugin(QmlDesigner bindingeditordialog.cpp bindingeditordialog.h bindingeditorwidget.cpp bindingeditorwidget.h connectionvisitor.cpp connectionvisitor.h + signallist.cpp signallist.h + signallistdialog.cpp signallistdialog.h + signallistdelegate.cpp signallistdelegate.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri index ff2920ffabc..2351d00c515 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri @@ -5,6 +5,9 @@ HEADERS += $$PWD/actioneditordialog.h HEADERS += $$PWD/bindingeditordialog.h HEADERS += $$PWD/bindingeditorwidget.h HEADERS += $$PWD/connectionvisitor.h +HEADERS += $$PWD/signallist.h +HEADERS += $$PWD/signallistdialog.h +HEADERS += $$PWD/signallistdelegate.h SOURCES += $$PWD/bindingeditor.cpp SOURCES += $$PWD/actioneditor.cpp @@ -13,3 +16,6 @@ SOURCES += $$PWD/actioneditordialog.cpp SOURCES += $$PWD/bindingeditordialog.cpp SOURCES += $$PWD/bindingeditorwidget.cpp SOURCES += $$PWD/connectionvisitor.cpp +SOURCES += $$PWD/signallist.cpp +SOURCES += $$PWD/signallistdialog.cpp +SOURCES += $$PWD/signallistdelegate.cpp diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp new file mode 100644 index 00000000000..c3f0ddb0e58 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** 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 "signallist.h" + +#include "signallistdelegate.h" + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +SignalListModel::SignalListModel(QObject *parent) + : QStandardItemModel(0, 3, parent) +{ + setHeaderData(TargetColumn, Qt::Horizontal, tr("Item ID")); + setHeaderData(SignalColumn, Qt::Horizontal, tr("Signal")); + setHeaderData(ButtonColumn, Qt::Horizontal, ""); +} + +void SignalListModel::setConnected(int row, bool connected) +{ + for (int col = 0; col < columnCount(); ++col) { + QModelIndex idx = index(row, col); + setData(idx, connected, ConnectedRole); + } +} + + +SignalListFilterModel::SignalListFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +bool SignalListFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex targetIndex = sourceModel()->index(sourceRow, SignalListModel::TargetColumn, sourceParent); + QModelIndex signalIndex = sourceModel()->index(sourceRow, SignalListModel::SignalColumn, sourceParent); + + return (sourceModel()->data(targetIndex).toString().contains(filterRegExp()) + || sourceModel()->data(signalIndex).toString().contains(filterRegExp())); +} + + +PropertyNameList SignalList::st_mouseSignals = { "clicked", "doubleClicked", "pressAndHold", + "pressed", "released", "wheel" }; + +SignalList::SignalList(QObject *) + : m_dialog(QPointer()) + , m_model(new SignalListModel(this)) + , m_modelNode() +{ +} + +SignalList::~SignalList() +{ + hideWidget(); +} + +void SignalList::prepareDialog() +{ + m_dialog = new SignalListDialog(Core::ICore::dialogParent()); + m_dialog->setAttribute(Qt::WA_DeleteOnClose); + m_dialog->initialize(m_model); + m_dialog->setWindowTitle(tr("Signal List for ") + m_modelNode.validId()); + + auto *delegate = static_cast(m_dialog->tableView()->itemDelegate()); + connect(delegate, &SignalListDelegate::connectClicked, this, &SignalList::connectClicked); +} + +void SignalList::showWidget() +{ + prepareDialog(); + m_dialog->show(); + m_dialog->raise(); +} + +void SignalList::hideWidget() +{ + if (m_dialog) + m_dialog->close(); + + m_dialog = nullptr; +} + +SignalList* SignalList::showWidget(const ModelNode &modelNode) +{ + auto signalList = new SignalList(); + signalList->setModelNode(modelNode); + signalList->prepareSignals(); + signalList->showWidget(); + + connect(signalList->m_dialog, &QDialog::destroyed, + [signalList]() { signalList->deleteLater(); } ); + + return signalList; +} + +void SignalList::setModelNode(const ModelNode &modelNode) +{ + if (modelNode.isValid()) + m_modelNode = modelNode; +} + +void SignalList::prepareSignals() +{ + if (!m_modelNode.isValid()) + return; + + AbstractView *view = m_modelNode.view(); + QList connections = getAssociatedConnections(view->allModelNodes()); + + for (ModelNode &node : view->allModelNodes()) { + // Collect all items which contain at least one of the specified signals + const PropertyNameList signalNames = node.metaInfo().signalNames(); + // Put the signals into a QSet to avoid duplicates + auto signalNamesSet = QSet(signalNames.begin(), signalNames.end()); + for (const PropertyName &signal : signalNamesSet) { + if (st_mouseSignals.contains(signal)) + appendSignalToModel(connections, node, signal); + } + + // Gather valid properties and aliases from components + for (const PropertyName &property : node.metaInfo().propertyNames()) { + const TypeName propertyType = node.metaInfo().propertyTypeName(property); + const NodeMetaInfo info = m_modelNode.model()->metaInfo(propertyType); + // Collect all items which contain at least one of the specified signals + const PropertyNameList signalNames = info.signalNames(); + // Put the signals into a QSet to avoid duplicates + auto signalNamesSet = QSet(signalNames.begin(), signalNames.end()); + for (const PropertyName &signal : signalNamesSet) { + if (st_mouseSignals.contains(signal)) + appendSignalToModel(connections, node, signal, property); + } + } + } +} + +void SignalList::connectClicked(const QModelIndex &modelIndex) +{ + auto proxyModel = static_cast(modelIndex.model()); + QModelIndex mappedModelIndex = proxyModel->mapToSource(modelIndex); + bool connected = mappedModelIndex.data(SignalListModel::ConnectedRole).toBool(); + + if (!connected) + addConnection(mappedModelIndex); + else + removeConnection(mappedModelIndex); +} + +void SignalList::appendSignalToModel(const QList &connections, + ModelNode &node, + const PropertyName &signal, + const PropertyName &property) +{ + QStandardItem *idItem = new QStandardItem(); + QString id(node.validId()); + if (!property.isEmpty()) + id += "." + QString::fromLatin1(property); + + idItem->setData(id, Qt::DisplayRole); + + QStandardItem *signalItem = new QStandardItem(); + signalItem->setData(signal, Qt::DisplayRole); + + QStandardItem *buttonItem = new QStandardItem(); + + idItem->setData(false, SignalListModel::ConnectedRole); + signalItem->setData(false, SignalListModel::ConnectedRole); + buttonItem->setData(false, SignalListModel::ConnectedRole); + + for (const QmlConnections &connection : connections) { + if (connection.target() == id) { + for (const SignalHandlerProperty &property : connection.signalProperties()) { + auto signalWithoutPrefix = SignalHandlerProperty::prefixRemoved(property.name()); + if (signalWithoutPrefix == signal) { + buttonItem->setData(connection.modelNode().internalId(), + SignalListModel::ConnectionsInternalIdRole); + + idItem->setData(true, SignalListModel::ConnectedRole); + signalItem->setData(true, SignalListModel::ConnectedRole); + buttonItem->setData(true, SignalListModel::ConnectedRole); + } + } + } + } + m_model->appendRow({idItem, signalItem, buttonItem}); +} + +void SignalList::addConnection(const QModelIndex &modelIndex) +{ + const QModelIndex targetModelIndex = modelIndex.siblingAtColumn(SignalListModel::TargetColumn); + const QModelIndex signalModelIndex = modelIndex.siblingAtColumn(SignalListModel::SignalColumn); + const QModelIndex buttonModelIndex = modelIndex.siblingAtColumn(SignalListModel::ButtonColumn); + const PropertyName signalName = m_model->data(signalModelIndex, + Qt::DisplayRole).toByteArray(); + + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_CONNECTION_ADDED); + + AbstractView *view = m_modelNode.view(); + const ModelNode rootModelNode = view->rootModelNode(); + + if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) { + NodeMetaInfo nodeMetaInfo = view->model()->metaInfo("QtQuick.Connections"); + if (nodeMetaInfo.isValid()) { + view->executeInTransaction("ConnectionModel::addConnection", [=, &rootModelNode](){ + ModelNode newNode = view->createModelNode("QtQuick.Connections", + nodeMetaInfo.majorVersion(), + nodeMetaInfo.minorVersion()); + const QString source = m_modelNode.validId() + ".trigger()"; + + if (QmlItemNode::isValidQmlItemNode(m_modelNode)) + m_modelNode.nodeAbstractProperty("data").reparentHere(newNode); + else + rootModelNode.nodeAbstractProperty(rootModelNode.metaInfo().defaultPropertyName()).reparentHere(newNode); + + const QString expression = m_model->data(targetModelIndex, Qt::DisplayRole).toString(); + newNode.bindingProperty("target").setExpression(expression); + newNode.signalHandlerProperty(SignalHandlerProperty::prefixAdded(signalName)).setSource(source); + + m_model->setConnected(modelIndex.row(), true); + m_model->setData(buttonModelIndex, newNode.internalId(), SignalListModel::ConnectionsInternalIdRole); + }); + } + } +} + +void SignalList::removeConnection(const QModelIndex &modelIndex) +{ + const QModelIndex signalModelIndex = modelIndex.siblingAtColumn(SignalListModel::SignalColumn); + const QModelIndex buttonModelIndex = modelIndex.siblingAtColumn(SignalListModel::ButtonColumn); + const PropertyName signalName = m_model->data(signalModelIndex, + Qt::DisplayRole).toByteArray(); + const int connectionInternalId = m_model->data(buttonModelIndex, + SignalListModel::ConnectionsInternalIdRole).toInt(); + + AbstractView *view = m_modelNode.view(); + const ModelNode connectionModelNode = view->modelNodeForInternalId(connectionInternalId); + SignalHandlerProperty targetSignal; + + if (connectionModelNode.isValid()) + targetSignal = connectionModelNode.signalHandlerProperty(signalName); + + ModelNode node = targetSignal.parentModelNode(); + if (node.isValid()) { + view->executeInTransaction("ConnectionModel::removeConnection", [=, &node](){ + QList allSignals = node.signalProperties(); + if (allSignals.size() > 1) { + const auto targetSignalWithPrefix = SignalHandlerProperty::prefixAdded(targetSignal.name()); + for (const SignalHandlerProperty &signal : allSignals) + if (signal.name() == targetSignalWithPrefix) + node.removeProperty(targetSignalWithPrefix); + } else { + node.destroy(); + } + m_model->setConnected(modelIndex.row(), false); + m_model->setData(buttonModelIndex, QVariant(), SignalListModel::ConnectionsInternalIdRole); + }); + } +} + +QList SignalList::getAssociatedConnections(const QList &nodes) +{ + return Utils::transform>(Utils::filtered(nodes, [this](const ModelNode &node) { + const QmlConnections connection(node); + if (!connection.isValid()) + return false; + + for (const SignalHandlerProperty &property : connection.signalProperties()) { + auto signalWithoutPrefix = SignalHandlerProperty::prefixRemoved(property.name()); + const QStringList sourceComponents = property.source().split("."); + QString sourceId; + QString sourceProperty; + if (sourceComponents.size() > 1) { + sourceId = sourceComponents[0]; + sourceProperty = sourceComponents[1]; + } + + if (st_mouseSignals.contains(signalWithoutPrefix) + && sourceId == m_modelNode.validId() + && sourceProperty == "trigger()") + return true; + } + + return false; + }), [](const ModelNode &node) { + return QmlConnections(node); + }); +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.h b/src/plugins/qmldesigner/components/bindingeditor/signallist.h new file mode 100644 index 00000000000..24103fc395a --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +class SignalListModel : public QStandardItemModel +{ + Q_OBJECT + +public: + enum ColumnRoles : unsigned int { + TargetColumn = 0, + SignalColumn = 1, + ButtonColumn = 2 + }; + enum UserRoles : unsigned int { + ConnectionsInternalIdRole = Qt::UserRole + 1, + ConnectedRole + }; + + SignalListModel(QObject *parent = nullptr); + + void setConnected(int row, bool connected); +}; + +class SignalListFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + SignalListFilterModel(QObject *parent = nullptr); + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; +}; + +class SignalList : public QObject +{ + Q_OBJECT + +public: + explicit SignalList(QObject *parent = nullptr); + ~SignalList(); + + static SignalList* showWidget(const ModelNode &modelNode); + + void setModelNode(const ModelNode &modelNode); + void connectClicked(const QModelIndex &modelIndex); + +private: + void prepareDialog(); + void showWidget(); + void hideWidget(); + + void prepareSignals(); + + void appendSignalToModel(const QList &connections, + ModelNode &node, + const PropertyName &signal, + const PropertyName &property = ""); + + void addConnection(const QModelIndex &modelIndex); + void removeConnection(const QModelIndex &modelIndex); + + QList getAssociatedConnections(const QList &nodes); + +private: + static PropertyNameList st_mouseSignals; + + QPointer m_dialog; + SignalListModel *m_model; + ModelNode m_modelNode; +}; + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.cpp new file mode 100644 index 00000000000..eb3bcffeb2e --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 "signallistdelegate.h" + +#include "signallist.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +SignalListDelegate::SignalListDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{} + +QWidget *SignalListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.column() == SignalListModel::ButtonColumn) + return nullptr; + + return QStyledItemDelegate::createEditor(parent, option, index); +} + +QRect connectButtonRect(const QStyleOptionViewItem &option) +{ + return option.rect.adjusted(3, 3, -3, -3); +} + +void SignalListDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + const bool connected = index.data(SignalListModel::ConnectedRole).toBool(); + if (connected) { + QStyleOptionViewItem opt(option); + opt.state = QStyle::State_Selected; + QStyledItemDelegate::paint(painter, opt, index); + if (index.column() != SignalListModel::ButtonColumn) + return; + } + if (index.column() == SignalListModel::ButtonColumn) { + QStyleOptionButton button; + button.rect = connectButtonRect(option); + button.text = connected ? tr("Release") : tr("Connect"); + button.state = QStyle::State_Enabled; + QApplication::style()->drawControl(QStyle::CE_PushButton, &button, painter); + return; + } + QStyledItemDelegate::paint(painter, option, index); +} + +bool SignalListDelegate::editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) +{ + Q_UNUSED(model) + + if (index.column() == SignalListModel::ButtonColumn + && event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + if (connectButtonRect(option).contains(mouseEvent->pos())) + emit connectClicked(index); + } + return true; +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.h b/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.h new file mode 100644 index 00000000000..039fb20b444 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallistdelegate.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +namespace QmlDesigner { + +class SignalListDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +signals: + void connectClicked(const QModelIndex &modelIndex) const; + +public: + SignalListDelegate(QObject *parent = nullptr); + + QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + bool editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) override; +}; + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.cpp new file mode 100644 index 00000000000..fde8c9649b8 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** 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 "signallistdialog.h" + +#include "signallist.h" +#include "signallistdelegate.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +QWidget *createFilterWidget(Utils::FancyLineEdit *lineEdit) +{ + const QString unicode = Theme::getIconUnicode(Theme::Icon::search); + const QString fontName = "qtds_propertyIconFont.ttf"; + QIcon icon = Utils::StyleHelper::getIconFromIconFont(fontName, unicode, 28, 28); + auto *label = new QLabel; + label->setPixmap(icon.pixmap(QSize(24, 24))); + label->setAlignment(Qt::AlignCenter); + lineEdit->setPlaceholderText(QObject::tr("", "Library search input hint text")); + lineEdit->setDragEnabled(false); + lineEdit->setMinimumWidth(75); + lineEdit->setTextMargins(0, 0, 20, 0); + lineEdit->setFiltering(true); + auto *box = new QHBoxLayout; + box->addWidget(label); + box->addWidget(lineEdit); + auto *widget = new QWidget; + widget->setLayout(box); + return widget; +} + +void modifyPalette(QTableView *view, const QColor &selectionColor) +{ + QPalette p = view->palette(); + p.setColor(QPalette::AlternateBase, p.color(QPalette::Base).lighter(120)); + p.setColor(QPalette::Highlight, selectionColor); + view->setPalette(p); + view->setAlternatingRowColors(true); +} + + +SignalListDialog::SignalListDialog(QWidget *parent) + : QDialog(parent) + , m_table(new QTableView()) + , m_searchLine(new Utils::FancyLineEdit()) +{ + auto *signalListDelegate = new SignalListDelegate(m_table); + m_table->setItemDelegate(signalListDelegate); + m_table->setFocusPolicy(Qt::NoFocus); + m_table->setSelectionMode(QAbstractItemView::NoSelection); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->verticalHeader()->hide(); + + modifyPalette(m_table, QColor("#d87b00")); + + auto *layout = new QVBoxLayout; + layout->addWidget(createFilterWidget(m_searchLine)); + layout->addWidget(m_table); + setLayout(layout); + + setWindowFlag(Qt::Tool, true); + setModal(true); + resize(600, 480); +} + +SignalListDialog::~SignalListDialog() +{ +} + +void SignalListDialog::initialize(QStandardItemModel *model) +{ + m_searchLine->clear(); + + auto *proxyModel = new SignalListFilterModel(this); + proxyModel->setSourceModel(model); + m_table->setModel(proxyModel); + + QHeaderView *header = m_table->horizontalHeader(); + header->setSectionResizeMode(SignalListModel::TargetColumn, QHeaderView::Stretch); + header->setSectionResizeMode(SignalListModel::SignalColumn, QHeaderView::Stretch); + header->resizeSection(SignalListModel::ButtonColumn, 120); + header->setStretchLastSection(false); + + auto eventFilterFun = [this](const QString &str) { + if (auto *fm = qobject_cast(m_table->model())) + fm->setFilterFixedString(str); + }; + connect(m_searchLine, &Utils::FancyLineEdit::filterChanged, eventFilterFun); +} + +QTableView *SignalListDialog::tableView() const +{ + return m_table; +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.h b/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.h new file mode 100644 index 00000000000..2b5ea88ebb1 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/signallistdialog.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QStandardItemModel; +class QTableView; +QT_END_NAMESPACE + +namespace Utils { +class FancyLineEdit; +} + +namespace QmlDesigner { + +class SignalListDialog : public QDialog +{ + Q_OBJECT + +public: + SignalListDialog(QWidget *parent = nullptr); + ~SignalListDialog() override; + + void initialize(QStandardItemModel *model); + + QTableView *tableView() const; + +private: + QTableView *m_table; + Utils::FancyLineEdit *m_searchLine; +}; + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index fd6ffa87696..721890e66bb 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -89,6 +89,8 @@ const char fitRootToScreenCommandId[] = "FitRootToScreen"; const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen"; const char editAnnotationCommandId[] = "EditAnnotation"; +const char openSignalDialogCommandId[] = "OpenSignalDialog"; + const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); const char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select Effect"); @@ -128,6 +130,8 @@ const char addSignalHandlerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContext const char moveToComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Move Component into Separate File"); const char editAnnotationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotation"); +const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog"); + const char setIdDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Id"); const char resetZDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset z Property"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 207ecbf357d..a8851f9eb51 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -564,6 +564,7 @@ const char yProperty[] = "y"; const char zProperty[] = "z"; const char widthProperty[] = "width"; const char heightProperty[] = "height"; +const char triggerSlot[] = "trigger"; using namespace SelectionContextFunctors; @@ -644,6 +645,14 @@ bool selectionNotEmptyAndHasXorYProperty(const SelectionContext &context) && selectionHasProperty1or2(context, xProperty, yProperty); } +bool singleSelectionAndHasSlotTrigger(const SelectionContext &context) +{ + if (!singleSelection(context)) + return false; + + return selectionHasSlot(context, triggerSlot); +} + bool singleSelectionAndInQtQuickLayout(const SelectionContext &context) { if (!singleSelection(context)) @@ -1384,6 +1393,16 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new ChangeStyleAction()); addDesignerAction(new EditListModelAction); + + addDesignerAction(new ModelNodeContextMenuAction( + openSignalDialogCommandId, + openSignalDialogDisplayName, + {}, + rootCategory, + QKeySequence(), + 66, + &openSignalDialog, + &singleSelectionAndHasSlotTrigger)); } void DesignerActionManager::createDefaultAddResourceHandler() diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 7fe891fd726..37696245849 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -30,6 +30,7 @@ #include "abstractactiongroup.h" #include "qmlitemnode.h" #include +#include #include @@ -80,12 +81,24 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState) inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property) { - foreach (const ModelNode &modelNode, selectionState.selectedModelNodes()) + for (const ModelNode &modelNode : selectionState.selectedModelNodes()) if (modelNode.hasProperty(PropertyName(property))) return true; return false; } +inline bool selectionHasSlot(const SelectionContext &selectionState, const char *property) +{ + for (const ModelNode &modelNode : selectionState.selectedModelNodes()) { + for (const PropertyName &slotName : modelNode.metaInfo().slotNames()) { + if (slotName == property) + return true; + } + } + + return false; +} + inline bool singleSelectedItem(const SelectionContext &selectionState) { QmlItemNode itemNode(selectionState.currentSingleSelectedNode()); @@ -94,7 +107,6 @@ inline bool singleSelectedItem(const SelectionContext &selectionState) bool selectionHasSameParent(const SelectionContext &selectionState); bool selectionIsComponent(const SelectionContext &selectionState); -bool selectionIsComponent(const SelectionContext &selectionState); bool singleSelectionItemIsAnchored(const SelectionContext &selectionState); bool singleSelectionItemIsNotAnchored(const SelectionContext &selectionState); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index f43e8fb49ec..2daf6931faa 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -87,6 +87,8 @@ #include #include +#include + namespace QmlDesigner { const PropertyName auxDataString("anchors_"); @@ -1550,6 +1552,14 @@ QVariant previewImageDataForImageNode(const ModelNode &modelNode) return {}; } +void openSignalDialog(const SelectionContext &selectionContext) +{ + if (!selectionContext.view() || !selectionContext.hasSingleSelectedModelNode()) + return; + + SignalList::showWidget(selectionContext.currentSingleSelectedNode()); +} + } // namespace ModelNodeOperations } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 1005445384c..8e6aac04c30 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -86,6 +86,8 @@ void mergeWithTemplate(const SelectionContext &selectionContext); void removeGroup(const SelectionContext &selectionContext); void editAnnotation(const SelectionContext &selectionContext); +void openSignalDialog(const SelectionContext &selectionContext); + // ModelNodePreviewImageOperations QVariant previewImageDataForGenericNode(const ModelNode &modelNode); QVariant previewImageDataForImageNode(const ModelNode &modelNode); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index e8226d6632e..41d98ca6082 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -49,9 +49,9 @@ namespace { QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList) { QStringList stringList; - for (const QmlDesigner::PropertyName &propertyName : propertyNameList) { + for (const QmlDesigner::PropertyName &propertyName : propertyNameList) stringList << QString::fromUtf8(propertyName); - } + stringList.removeDuplicates(); return stringList; } diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index 34e3c9881ee..a93522410f3 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -133,8 +133,6 @@ static QRect drawText(QPainter *painter, int iconOffset) { QString displayString = modelIndex.data(Qt::DisplayRole).toString(); - QPoint displayStringOffset; - int width = 0; // Check text length does not exceed available space const int extraSpace = 12 + iconOffset; @@ -142,10 +140,8 @@ static QRect drawText(QPainter *painter, displayString = styleOption.fontMetrics.elidedText(displayString, Qt::ElideMiddle, styleOption.rect.width() - extraSpace); - displayStringOffset = QPoint(5 + iconOffset, -5 - delegateMargin); - - width = styleOption.fontMetrics.horizontalAdvance(displayString); - + const QPoint displayStringOffset = QPoint(5 + iconOffset, -5 - delegateMargin); + const int width = styleOption.fontMetrics.horizontalAdvance(displayString); const QPoint textPosition = styleOption.rect.bottomLeft() + displayStringOffset; painter->drawText(textPosition, displayString); diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri index 981025d1fb5..1fe37967182 100644 --- a/src/plugins/qmldesigner/designercore/designercore-lib.pri +++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri @@ -69,6 +69,7 @@ SOURCES += $$PWD/model/abstractview.cpp \ $$PWD/model/qmlmodelnodefacade.cpp \ $$PWD/model/qmlobjectnode.cpp \ $$PWD/model/qmlanchors.cpp \ + $$PWD/model/qmlconnections.cpp \ $$PWD/rewritertransaction.cpp \ $$PWD/model/rewriteaction.cpp \ $$PWD/model/modelnodepositionstorage.cpp \ @@ -151,6 +152,7 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \ $$PWD/include/forwardview.h \ $$PWD/include/qmlobjectnode.h \ $$PWD/include/qmlanchors.h \ + $$PWD/include/qmlconnections.h \ $$PWD/rewritertransaction.h \ $$PWD/model/rewriteaction.h \ $$PWD/include/modelnodepositionstorage.h \ diff --git a/src/plugins/qmldesigner/designercore/include/qmlconnections.h b/src/plugins/qmldesigner/designercore/include/qmlconnections.h new file mode 100644 index 00000000000..d84f0017a9b --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/qmlconnections.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include "qmlmodelnodefacade.h" + +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT QmlConnections : public QmlModelNodeFacade +{ + friend class StatesEditorView; + +public: + QmlConnections(); + QmlConnections(const ModelNode &modelNode); + + bool isValid() const override; + static bool isValidQmlConnections(const ModelNode &modelNode); + void destroy(); + + QString target() const; + void setTarget(const QString &target); + + ModelNode getTargetNode() const; + + QList signalProperties() const; + + static ModelNode createQmlConnections(AbstractView *view); +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h b/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h index 47763a37f2e..f28fd7feda7 100644 --- a/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h +++ b/src/plugins/qmldesigner/designercore/include/signalhandlerproperty.h @@ -43,6 +43,9 @@ public: SignalHandlerProperty(); SignalHandlerProperty(const SignalHandlerProperty &property, AbstractView *view); + static PropertyName prefixAdded(const PropertyName &propertyName); + static PropertyName prefixRemoved(const PropertyName &propertyName); + protected: SignalHandlerProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); }; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 3fe1f396b28..9aec482f06b 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -82,7 +82,7 @@ using PropertyInfo = QPair; QVector getObjectTypes(const ObjectValue *ov, const ContextPtr &context, bool local = false, int rec = 0); -static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPtr &context, QVector &dotProperties) +static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPtr &context, QVector &dotProperties) { TypeName type = "unknown"; @@ -295,7 +295,7 @@ public: const TypeName type = resolveTypeName(ref, m_context, dotProperties); m_properties.append({propertyName, type}); if (!dotProperties.isEmpty()) { - foreach (const PropertyInfo &propertyInfo, dotProperties) { + for (const PropertyInfo &propertyInfo : qAsConst(dotProperties)) { PropertyName dotName = propertyInfo.first; TypeName type = propertyInfo.second; dotName = propertyName + '.' + dotName; @@ -303,7 +303,6 @@ public: } } } else { - if (const CppComponentValue * cppComponentValue = value_cast(value)) { TypeName qualifiedTypeName = qualifiedTypeNameForContext(cppComponentValue, m_context->viewerContext(), *m_context->snapshot().importDependencies()).toUtf8(); @@ -311,13 +310,13 @@ public: } else { TypeId typeId; TypeName typeName = typeId(value).toUtf8(); - if (typeName == "number") { - if (value->asIntValue()) { - typeName = "int"; - } else { - typeName = "real"; - } - } + + if (typeName == "Function") + return processSlot(name, value); + + if (typeName == "number") + typeName = value->asIntValue() ? "int" : "real"; + m_properties.append({propertyName, typeName}); } } @@ -488,7 +487,7 @@ PropertyNameList getSignals(const ObjectValue *objectValue, const ContextPtr &co QList objects = prototypeIterator.all(); if (!local) { - foreach (const ObjectValue *prototype, objects) + for (const ObjectValue *prototype : objects) signalList.append(getSignals(prototype, context, true)); } @@ -507,6 +506,9 @@ PropertyNameList getSlots(const ObjectValue *objectValue, const ContextPtr &cont PropertyMemberProcessor processor(context); objectValue->processMembers(&processor); + if (const ASTObjectValue *astObjectValue = objectValue->asAstObjectValue()) + astObjectValue->processMembers(&processor); + slotList.append(processor.slotList()); PrototypeIterator prototypeIterator(objectValue, context); @@ -534,6 +536,7 @@ QVector getObjectTypes(const ObjectValue *objectValue, const Conte PropertyMemberProcessor processor(context); objectValue->processMembers(&processor); + const auto props = processor.properties(); for (const PropertyInfo &property : props) { diff --git a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp new file mode 100644 index 00000000000..0a0f88ea7ab --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** 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 "qmlconnections.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +QmlConnections::QmlConnections() +{ +} + +QmlConnections::QmlConnections(const ModelNode &modelNode) + : QmlModelNodeFacade(modelNode) +{ +} + +bool QmlConnections::isValid() const +{ + return isValidQmlConnections(modelNode()); +} + +bool QmlConnections::isValidQmlConnections(const ModelNode &modelNode) +{ + return isValidQmlModelNodeFacade(modelNode) + && modelNode.metaInfo().isValid() + && (modelNode.type() == "Connections" + || modelNode.type() == "QtQuick.Connections" + || modelNode.type() == "Qt.Connections" + || modelNode.type() == "QtQml.Connections"); +} + +/*! + Removes connections node. +*/ +void QmlConnections::destroy() +{ + Q_ASSERT(isValid()); + modelNode().destroy(); +} + +QString QmlConnections::target() const +{ + if (modelNode().isValid()) { + const BindingProperty bindingproperty = modelNode().bindingProperty("target"); + if (bindingproperty.isValid()) + return bindingproperty.expression(); + } + + return QString(); +} + +void QmlConnections::setTarget(const QString &target) +{ + modelNode().bindingProperty("target").setExpression(target); +} + +ModelNode QmlConnections::getTargetNode() const +{ + ModelNode result; + + if (!modelNode().isValid()) + return result; + + const BindingProperty bindingProperty = modelNode().bindingProperty("target"); + const QString bindExpression = bindingProperty.expression(); + + if (bindingProperty.isValid()) { + AbstractView *view = modelNode().view(); + if (bindExpression.contains(".")) { + QStringList substr = bindExpression.split("."); + const QString itemId = substr.constFirst(); + if (substr.size() > 1) { + const ModelNode aliasParent = view->modelNodeForId(itemId); + substr.removeFirst(); // remove id, only alias pieces left + const QString aliasBody = substr.join("."); + if (aliasParent.isValid() && aliasParent.hasBindingProperty(aliasBody.toUtf8())) { + const BindingProperty binding = aliasParent.bindingProperty(aliasBody.toUtf8()); + if (binding.isValid() && view->hasId(binding.expression())) + result = view->modelNodeForId(binding.expression()); + } + } + } else { + result = view->modelNodeForId(bindExpression); + } + } + + return result; +} + +QList QmlConnections::signalProperties() const +{ + return modelNode().signalProperties(); +} + +ModelNode QmlConnections::createQmlConnections(AbstractView *view) +{ + NodeMetaInfo nodeMetaInfo = view->model()->metaInfo("QtQuick.Connections"); + return view->createModelNode("QtQuick.Connections", + nodeMetaInfo.majorVersion(), + nodeMetaInfo.minorVersion()); +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp index e1701a0b710..d8d7e2c9359 100644 --- a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp @@ -40,13 +40,11 @@ SignalHandlerProperty::SignalHandlerProperty(const SignalHandlerProperty &proper { } - SignalHandlerProperty::SignalHandlerProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view) : AbstractProperty(propertyName, internalNode, model, view) { } - void SignalHandlerProperty::setSource(const QString &source) { Internal::WriteLocker locker(model()); @@ -83,4 +81,30 @@ QString SignalHandlerProperty::source() const return QString(); } +PropertyName SignalHandlerProperty::prefixAdded(const PropertyName &propertyName) +{ + QString nameAsString = QString::fromUtf8(propertyName); + if (nameAsString.startsWith("on")) + return propertyName; + + QChar firstChar = nameAsString.at(0).toUpper(); + nameAsString[0] = firstChar; + nameAsString.prepend("on"); + + return nameAsString.toLatin1(); +} + +PropertyName SignalHandlerProperty::prefixRemoved(const PropertyName &propertyName) +{ + QString nameAsString = QString::fromUtf8(propertyName); + if (!nameAsString.startsWith("on")) + return propertyName; + + nameAsString.remove(0, 2); + QChar firstChar = nameAsString.at(0).toLower(); + nameAsString[0] = firstChar; + + return nameAsString.toLatin1(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 1463b169457..e4ba26775b7 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -302,6 +302,7 @@ Project { "include/qmlmodelnodefacade.h", "include/qmlobjectnode.h", "include/qmlstate.h", + "include/qmlconnections.h", "include/removebasestateexception.h", "include/documentmessage.h", "include/rewriterview.h", @@ -391,6 +392,7 @@ Project { "model/qmlmodelnodefacade.cpp", "model/qmlobjectnode.cpp", "model/qmlstate.cpp", + "model/qmlconnections.cpp", "model/qmltextgenerator.cpp", "model/qmltextgenerator.h", "model/rewriteaction.cpp", @@ -739,6 +741,12 @@ Project { "bindingeditor/bindingeditorwidget.h", "bindingeditor/connectionvisitor.cpp", "bindingeditor/connectionvisitor.h", + "bindingeditor/signallist.cpp", + "bindingeditor/signallist.h", + "bindingeditor/signallistdialog.cpp", + "bindingeditor/signallistdialog.h", + "bindingeditor/signallistdelegate.cpp", + "bindingeditor/signallistdelegate.h", "colortool/colortool.cpp", "colortool/colortool.h", "connectioneditor/addnewbackenddialog.h",