diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6dc4a894664..d4d498d503b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -780,7 +780,27 @@ extend_qtc_plugin(QmlDesigner detail/treeitemdelegate.cpp detail/treeitemdelegate.h detail/treemodel.cpp detail/treemodel.h detail/treeview.cpp detail/treeview.h - detail/utils.cpp detail/utils.h + detail/curveeditorutils.cpp detail/curveeditorutils.h +) + +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/eventlist + SOURCES + eventlistplugin.qrc + eventlistpluginview.cpp eventlistpluginview.h + assigneventdialog.cpp assigneventdialog.h + eventlist.cpp eventlist.h + eventlistactions.cpp eventlistactions.h + eventlistdelegate.cpp eventlistdelegate.h + eventlistdialog.cpp eventlistdialog.h + eventlistview.cpp eventlistview.h + eventlistutils.cpp eventlistutils.h + filterlinewidget.cpp filterlinewidget.h + nodelistdelegate.cpp nodelistdelegate.h + nodelistview.cpp nodelistview.h + nodeselectionmodel.cpp nodeselectionmodel.h + connectsignaldialog.cpp connectsignaldialog.h + shortcutwidget.cpp shortcutwidget.h ) # Do the file comparison at the end, due to all the extend_qtc_plugin calls diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 9024b32776e..4fd6fdf1596 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -46,6 +46,7 @@ const char flowEffectCategory[] = "FlowEffect"; const char flowConnectionCategory[] = "FlowConnection"; const char stackedContainerCategory[] = "StackedContainer"; const char genericToolBarCategory[] = "GenericToolBar"; +const char eventListCategory[] = "QmlEventList"; const char toFrontCommandId[] = "ToFront"; const char toBackCommandId[] = "ToBack"; @@ -209,6 +210,7 @@ const int priorityGroupCategory = 140; const int priorityPositionCategory = 130; const int priorityLayoutCategory = 120; const int priorityStackedContainerCategory = priorityLayoutCategory; +const int priorityEventListCategory = 105; const int priorityTopLevelSeperator = 100; const int priorityCustomActions = 80; const int priorityRefactoring = 60; diff --git a/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp index ecaf9036555..3e910638daf 100644 --- a/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp @@ -25,7 +25,7 @@ #include "animationcurve.h" #include "curvesegment.h" -#include "detail/utils.h" +#include "detail/curveeditorutils.h" #include #include diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri index 2cef92b01b1..2ad4018d580 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri @@ -45,7 +45,7 @@ SOURCES += \ $$PWD/detail/treeitemdelegate.cpp \ $$PWD/detail/treemodel.cpp \ $$PWD/detail/treeview.cpp \ - $$PWD/detail/utils.cpp \ + $$PWD/detail/curveeditorutils.cpp \ $$PWD/detail/axis.cpp \ $$PWD/keyframe.cpp \ $$PWD/treeitem.cpp diff --git a/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp b/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp index cf2861991bd..cccf6eaf416 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp @@ -24,7 +24,7 @@ ****************************************************************************/ #include "curvesegment.h" -#include "detail/utils.h" +#include "detail/curveeditorutils.h" #include #include diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorutils.cpp similarity index 100% rename from src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp rename to src/plugins/qmldesigner/components/curveeditor/detail/curveeditorutils.cpp diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/utils.h b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorutils.h similarity index 100% rename from src/plugins/qmldesigner/components/curveeditor/detail/utils.h rename to src/plugins/qmldesigner/components/curveeditor/detail/curveeditorutils.h diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp index 5a0d7c40434..ef8cf9f2f29 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp @@ -26,7 +26,7 @@ #include "animationcurve.h" #include "graphicsscene.h" #include "keyframeitem.h" -#include "utils.h" +#include "curveeditorutils.h" #include #include diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp index 092fd423587..23daabba66e 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp @@ -29,7 +29,7 @@ #include "curveitem.h" #include "navigation2d.h" #include "treeitem.h" -#include "utils.h" +#include "curveeditorutils.h" #include #include diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp index 6d9dbe85d5d..ab62f0b673f 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp @@ -27,7 +27,7 @@ #include "curvesegment.h" #include "graphicsscene.h" #include "keyframeitem.h" -#include "utils.h" +#include "curveeditorutils.h" #include diff --git a/src/plugins/qmldesigner/components/eventlist/assigneventdialog.cpp b/src/plugins/qmldesigner/components/eventlist/assigneventdialog.cpp new file mode 100644 index 00000000000..81eb4ff7f2a --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/assigneventdialog.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "assigneventdialog.h" +#include "eventlist.h" +#include "eventlistdelegate.h" +#include "eventlistview.h" +#include "filterlinewidget.h" +#include "nodelistdelegate.h" +#include "nodelistview.h" +#include "nodeselectionmodel.h" +#include "eventlistutils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +AssignEventDialog::AssignEventDialog(QWidget *parent) + : QDialog(parent) + , m_nodeTable(new QTableView) + , m_eventTable(new QTableView) + , m_nodeLine(new FilterLineWidget()) + , m_eventLine(new FilterLineWidget()) +{ + setWindowFlag(Qt::Tool, true); + setModal(true); + + auto *nodeFilterModel = new QSortFilterProxyModel; + auto *nodeListDelegate = new NodeListDelegate(m_nodeTable); + auto *nodeSelectionModel = new NodeSelectionModel(nodeFilterModel); + m_nodeTable->installEventFilter(new TabWalker(this)); + m_nodeTable->setItemDelegate(nodeListDelegate); + m_nodeTable->setModel(nodeFilterModel); + m_nodeTable->setSelectionModel(nodeSelectionModel); + m_nodeTable->setFocusPolicy(Qt::NoFocus); + m_nodeTable->setSelectionMode(QAbstractItemView::SingleSelection); + m_nodeTable->setSelectionBehavior(QAbstractItemView::SelectRows); + m_nodeTable->resizeColumnsToContents(); + m_nodeTable->horizontalHeader()->setStretchLastSection(true); + m_nodeTable->verticalHeader()->hide(); + polishPalette(m_nodeTable, "#1f75cc"); + + auto *eventFilterModel = new QSortFilterProxyModel; + auto *eventListDelegate = new EventListDelegate(m_eventTable); + m_eventTable->installEventFilter(new TabWalker(this)); + m_eventTable->setItemDelegate(eventListDelegate); + m_eventTable->setFocusPolicy(Qt::NoFocus); + m_eventTable->setSelectionMode(QAbstractItemView::NoSelection); + m_eventTable->setSelectionBehavior(QAbstractItemView::SelectRows); + m_eventTable->setModel(eventFilterModel); + m_eventTable->verticalHeader()->hide(); + polishPalette(m_eventTable, QColor("#d87b00")); + + auto *nodeBox = new QVBoxLayout; + nodeBox->addWidget(m_nodeLine); + nodeBox->addWidget(m_nodeTable); + + QWidget *nodeWidget = new QWidget; + nodeWidget->setLayout(nodeBox); + + auto *eventBox = new QVBoxLayout; + eventBox->addWidget(m_eventLine); + eventBox->addWidget(m_eventTable); + + auto *eventWidget = new QWidget; + eventWidget->setLayout(eventBox); + + auto *splitter = new QSplitter(Qt::Horizontal); + splitter->addWidget(nodeWidget); + splitter->addWidget(eventWidget); + splitter->setStretchFactor(0, 1); + splitter->setStretchFactor(1, 3); + + auto *box = new QHBoxLayout; + box->addWidget(splitter); + setLayout(box); + + connect(m_nodeLine, &FilterLineWidget::filterChanged, [this](const QString &str) { + if (auto *sm = qobject_cast(m_nodeTable->selectionModel())) { + sm->storeSelection(); + if (auto *fm = qobject_cast(m_nodeTable->model())) + fm->setFilterFixedString(str); + sm->reselect(); + } + }); + + connect(m_eventLine, &FilterLineWidget::filterChanged, [this](const QString &str) { + if (auto *fm = qobject_cast(m_eventTable->model())) + fm->setFilterFixedString(str); + }); + + connect(eventListDelegate, &EventListDelegate::connectClicked, + [](const QString &id, bool connected) { + if (connected) + EventList::addEventIdToCurrent(id); + else + EventList::removeEventIdFromCurrent(id); + }); +} + +void AssignEventDialog::initialize(EventList &events) +{ + m_nodeLine->clear(); + m_eventLine->clear(); + + if (auto *fm = qobject_cast(m_nodeTable->model())) + fm->setSourceModel(events.nodeModel()); + + if (auto *fm = qobject_cast(m_eventTable->model())) + fm->setSourceModel(events.view()->eventListModel()); + + if (auto *sm = qobject_cast(m_nodeTable->selectionModel())) { + if (m_connection) + sm->disconnect(m_connection); + + auto updateEventListView = [this, &events](const QStringList &eventIds) { + auto nonExistent = events.view()->eventListModel()->connectEvents(eventIds); + + if (!nonExistent.empty()) { + QString header(tr("Nonexistent events discovered")); + QString msg(tr("The Node references the following nonexistent events:\n")); + for (auto &&id : nonExistent) + msg += id + ", "; + msg.remove(msg.size() - 2, 2); + msg += "\nDo you want to remove these references?"; + + if (QMessageBox::Yes == QMessageBox::question(this, header, msg)) { + auto *view = events.nodeListView(); + view->removeEventIds(view->currentNode(), nonExistent); + view->reset(); + if (auto *sm = qobject_cast(m_nodeTable->selectionModel())) + sm->selectNode(view->currentNode()); + } + } + m_eventTable->update(); + }; + m_connection = connect(sm, &NodeSelectionModel::nodeSelected, updateEventListView); + } + + m_nodeTable->setColumnHidden(NodeListModel::typeColumn, true); + m_nodeTable->setColumnHidden(NodeListModel::fromColumn, true); + m_nodeTable->setColumnHidden(NodeListModel::toColumn, true); + + if (QHeaderView *header = m_eventTable->horizontalHeader()) { + header->setSectionResizeMode(EventListModel::idColumn, QHeaderView::Stretch); + header->setSectionResizeMode(EventListModel::descriptionColumn, QHeaderView::Stretch); + header->setSectionResizeMode(EventListModel::shortcutColumn, QHeaderView::Stretch); + header->resizeSection(EventListModel::connectColumn, 120); + header->setStretchLastSection(false); + } +} + +void AssignEventDialog::postShow() +{ + if (auto *sm = qobject_cast(m_nodeTable->selectionModel())) + sm->selectNode(EventList::currentNode()); + + resize(QSize(700, 300)); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/assigneventdialog.h b/src/plugins/qmldesigner/components/eventlist/assigneventdialog.h new file mode 100644 index 00000000000..13196c5d234 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/assigneventdialog.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include + +QT_FORWARD_DECLARE_CLASS(QTableView) + +namespace QmlDesigner { + +class EventList; +class FilterLineWidget; + +class AssignEventDialog : public QDialog +{ + Q_OBJECT + +public: + AssignEventDialog(QWidget *parent = nullptr); + void initialize(EventList &events); + void postShow(); + +private: + QTableView *m_nodeTable; + QTableView *m_eventTable; + + FilterLineWidget *m_nodeLine; + FilterLineWidget *m_eventLine; + + QMetaObject::Connection m_connection; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.cpp b/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.cpp new file mode 100644 index 00000000000..3a1d8a70c66 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "connectsignaldialog.h" +#include "eventlist.h" +#include "eventlistdelegate.h" +#include "eventlistview.h" +#include "filterlinewidget.h" +#include "nodelistview.h" +#include "eventlistutils.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +QString eventListToSource(const QStringList &events) +{ + if (events.empty()) + return QString("{}"); + + QString source("{\n"); + for (auto &&event : events) + source += QString("EventSystem.triggerEvent(\"") + event + QString("\")\n"); + source += "}"; + return source; +} + +QStringList eventListFromSource(const QString &source) +{ + QStringList out; + for (auto &&substr : source.split("\n", Qt::SkipEmptyParts)) { + auto trimmed = substr.trimmed(); + if (trimmed.startsWith("EventSystem.triggerEvent(")) + out << trimmed.section('\"', 1, 1); + } + return out; +} + +ConnectSignalDialog::ConnectSignalDialog(QWidget *parent) + : QDialog(parent) + , m_table(new QTableView) + , m_filter(new FilterLineWidget()) + , m_property() +{ + setWindowFlag(Qt::Tool, true); + setModal(true); + + auto *filterModel = new QSortFilterProxyModel; + auto *delegate = new EventListDelegate(m_table); + + m_table->installEventFilter(new TabWalker(this)); + m_table->setItemDelegate(delegate); + m_table->setModel(filterModel); + m_table->setFocusPolicy(Qt::NoFocus); + m_table->setSelectionMode(QAbstractItemView::NoSelection); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->verticalHeader()->hide(); + polishPalette(m_table, QColor("#d87b00")); + + auto *box = new QVBoxLayout; + box->addWidget(m_filter); + box->addWidget(m_table); + + setLayout(box); + + connect(m_filter, &FilterLineWidget::filterChanged, [this](const QString &str) { + if (auto *fm = qobject_cast(m_table->model())) + fm->setFilterFixedString(str); + }); + + connect(delegate, + &EventListDelegate::connectClicked, + [this, filterModel](const QString &, bool) { + if (m_property.isValid()) { + if (const auto *m = qobject_cast( + filterModel->sourceModel())) { + QString source = eventListToSource(m->connectedEvents()); + EventList::setSignalSource(m_property, source); + } + } + }); +} + +void ConnectSignalDialog::initialize(EventList &events, const SignalHandlerProperty &signal) +{ + m_filter->clear(); + + auto *eventModel = events.view()->eventListModel(); + if (!eventModel) + return; + + if (auto *fm = qobject_cast(m_table->model())) + fm->setSourceModel(eventModel); + + m_property = signal; + if (m_property.isValid()) { + QString title = QString::fromUtf8(m_property.name()); + setWindowTitle(title); + eventModel->connectEvents(eventListFromSource(m_property.source())); + } + + if (QHeaderView *header = m_table->horizontalHeader()) { + header->setSectionResizeMode(EventListModel::idColumn, QHeaderView::Stretch); + header->setSectionResizeMode(EventListModel::descriptionColumn, QHeaderView::Stretch); + header->setSectionResizeMode(EventListModel::shortcutColumn, QHeaderView::Stretch); + header->resizeSection(EventListModel::connectColumn, 120); + header->setStretchLastSection(false); + } +} + +QSize ConnectSignalDialog::sizeHint() const +{ + return QSize(522, 270); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.h b/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.h new file mode 100644 index 00000000000..44f6527ff74 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/connectsignaldialog.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include "signalhandlerproperty.h" +#include + +QT_FORWARD_DECLARE_CLASS(QTableView) + +namespace QmlDesigner { + +class EventList; +class FilterLineWidget; + +class ConnectSignalDialog : public QDialog +{ + Q_OBJECT + +public: + ConnectSignalDialog(QWidget *parent = nullptr); + void initialize(EventList &events, const SignalHandlerProperty &signal); + +protected: + QSize sizeHint() const override; + +private: + QTableView *m_table; + FilterLineWidget *m_filter; + SignalHandlerProperty m_property; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp new file mode 100644 index 00000000000..6b7a53ba716 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "eventlist.h" +#include "eventlistpluginview.h" +#include "eventlistview.h" +#include "nodelistview.h" + +#include "bindingproperty.h" +#include "metainfo.h" +#include "projectexplorer/project.h" +#include "projectexplorer/session.h" +#include "qmldesignerplugin.h" +#include "signalhandlerproperty.h" +#include "utils/fileutils.h" +#include "utils/qtcassert.h" +#include "variantproperty.h" + +#include +#include + +namespace QmlDesigner { + +Utils::FilePath projectFilePath() +{ + if (auto *doc = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()) { + if (auto *proj = ProjectExplorer::SessionManager::projectForFile(doc->fileName())) + return proj->projectDirectory(); + } + return Utils::FilePath(); +} + +static Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) +{ + QDirIterator it(path.toString(), QDirIterator::Subdirectories); + + while (it.hasNext()) { + QFileInfo file(it.next()); + if (file.isDir()) + continue; + + if (file.fileName() == fileName) + return Utils::FilePath::fromFileInfo(file); + } + return {}; +} + + +NodeListView *EventList::st_nodeView = nullptr; + +void EventList::setNodeProperties(AbstractView *view) +{ + st_nodeView = new NodeListView(view); +} + +void EventList::selectNode(int internalId) +{ + if (st_nodeView) + st_nodeView->selectNode(internalId); +} + +int EventList::currentNode() +{ + if (st_nodeView) + return st_nodeView->currentNode(); + + return -1; +} + +bool EventList::hasEventListModel() +{ + Utils::FilePath projectPath = projectFilePath(); + if (projectPath.isEmpty()) + return false; + + Utils::FilePath path = findFile(projectPath, "EventListModel.qml"); + return path.exists(); +} + +void EventList::addEventIdToCurrent(const QString &eventId) +{ + int iid = currentNode(); + if (st_nodeView && iid >= 0) + st_nodeView->addEventId(iid, eventId); +} + +void EventList::removeEventIdFromCurrent(const QString &eventId) +{ + int iid = currentNode(); + if (st_nodeView && iid >= 0) + st_nodeView->removeEventIds(iid, {eventId}); +} + +QString EventList::setNodeId(int internalId, const QString &id) +{ + if (st_nodeView) + return st_nodeView->setNodeId(internalId, id); + + return QString(); +} + +QStandardItemModel *EventList::nodeModel() +{ + if (st_nodeView) + return st_nodeView->itemModel(); + + return nullptr; +} + +NodeListView *EventList::nodeListView() +{ + return st_nodeView; +} + +ModelNode EventList::modelNode(const QString &id) +{ + if (st_nodeView) + return st_nodeView->modelNodeForId(id); + + return ModelNode(); +} + +void EventList::setSignalSource(SignalHandlerProperty &prop, const QString &source) +{ + if (st_nodeView) { + QmlDesigner::Import import = + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.EventSystem", "1.0"); + + if (!st_nodeView->model()->hasImport(import, true, true)) { + try { + st_nodeView->model()->changeImports({import}, {}); + } catch (const QmlDesigner::Exception &) { + QTC_ASSERT(false, return ); + } + } + + if (source == "{}") { + if (ModelNode node = prop.parentModelNode(); node.isValid()) { + st_nodeView->executeInTransaction("EventList::removeProperty", + [&]() { node.removeProperty(prop.name()); }); + } + } + else { + st_nodeView->executeInTransaction("EventList::setSource", + [&]() { prop.setSource(source); }); + } + } +} + +EventList::EventList() + : m_model(nullptr) + , m_eventView(nullptr) + , m_path() + +{} + +Model *EventList::model() const +{ + return m_model; +} + +EventListView *EventList::view() const +{ + return m_eventView; +} + +QString EventList::read() const +{ + if (!m_path.exists()) + return QString(); + + Utils::FileReader reader; + QTC_ASSERT(reader.fetch(m_path), return QString()); + + return QString::fromUtf8(reader.data()); +} + + +void EventList::initialize(EventListPluginView *parent) +{ + Utils::FilePath projectPath = projectFilePath(); + QTC_ASSERT(!projectPath.isEmpty(), return ); + m_path = findFile(projectPath, "EventListModel.qml"); + + if (!m_model) { + QByteArray unqualifiedTypeName = "ListModel"; + auto metaInfo = parent->model()->metaInfo(unqualifiedTypeName); + + QByteArray fullTypeName = metaInfo.typeName(); + int minorVersion = metaInfo.minorVersion(); + int majorVersion = metaInfo.majorVersion(); + + m_model = Model::create(fullTypeName, majorVersion, minorVersion); + m_model->setParent(parent); + } + + if (!m_eventView) { + m_eventView = new EventListView(m_model); + m_model->attachView(m_eventView); + } +} + +void EventList::write(const QString &text) +{ + if (!m_path.exists()) + return; + + Utils::FileSaver writer(m_path); + writer.write(text.toUtf8()); + writer.finalize(); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.h b/src/plugins/qmldesigner/components/eventlist/eventlist.h new file mode 100644 index 00000000000..957ad6a39d7 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include "modelnode.h" +#include "utils/fileutils.h" + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QStandardItemModel) + +namespace QmlDesigner { + +class Model; +class NodeListView; +class EventListView; +class EventListPluginView; + +class EventList +{ +public: + static int currentNode(); + static bool hasEventListModel(); + static bool connectedToCurrent(const QString &eventId); + static void addEventIdToCurrent(const QString &eventId); + static void removeEventIdFromCurrent(const QString &eventId); + static void setNodeProperties(AbstractView *view); + static void selectNode(int internalId); + + static QString setNodeId(int internalId, const QString &id); + static QStandardItemModel *nodeModel(); + static NodeListView *nodeListView(); + static ModelNode modelNode(const QString &id); + + static void setSignalSource(SignalHandlerProperty &prop, const QString &source); + + EventList(); + + Model *model() const; + + EventListView *view() const; + QString read() const; + + void initialize(EventListPluginView *parent); + void write(const QString &text); + +private: + static NodeListView *st_nodeView; + + Model *m_model; + EventListView *m_eventView; + Utils::FilePath m_path; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp new file mode 100644 index 00000000000..5596674500e --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#include "eventlistactions.h" +#include "eventlist.h" +#include + +#include "metainfo.h" +#include + +#include + +namespace QmlDesigner { + +inline bool eventListEnabled(const SelectionContext &) +{ + return EventList::hasEventListModel(); +} + +QIcon eventListIconFromIconFont(Theme::Icon iconType) +{ + const QColor enabledColor(Theme::getColor(Theme::IconsBaseColor)); + const QColor disabledColor(Theme::getColor(Theme::IconsDisabledColor)); + const QString unicode = Theme::getIconUnicode(iconType); + const QString fontName = "qtds_propertyIconFont.ttf"; + + const auto enabledHelper = Utils::StyleHelper::IconFontHelper( + unicode, enabledColor, QSize(28, 28), QIcon::Normal); + + const auto disabledHelper = Utils::StyleHelper::IconFontHelper( + unicode, disabledColor, QSize(28, 28), QIcon::Disabled); + + return Utils::StyleHelper::getIconFromIconFont(fontName, {enabledHelper, disabledHelper}); +} + +static QIcon editEventListIcon() +{ + return eventListIconFromIconFont(Theme::Icon::edit); +} + +static void handleAction(const SelectionContext &) {} + +EventListAction::EventListAction() + : ModelNodeAction("EventList", + QObject::tr("Show Event List"), + editEventListIcon(), + QObject::tr("Show Event List"), + ComponentCoreConstants::eventListCategory, + QKeySequence("Alt+e"), + 230, + &handleAction, + &eventListEnabled) +{} + +static QIcon assignEventListIcon() +{ + return eventListIconFromIconFont(Theme::Icon::assign); +} + +static void handleAssignEventActionOperation(const SelectionContext &context) +{ + EventList::setNodeProperties(context.view()); +} + +AssignEventEditorAction::AssignEventEditorAction() + : ModelNodeAction("AssignEventEditor", + QObject::tr("Assign Events to Actions"), + assignEventListIcon(), + QObject::tr("Assign Events to Actions"), + ComponentCoreConstants::eventListCategory, + QKeySequence("Alt+a"), + 220, + &handleAssignEventActionOperation, + &eventListEnabled) +{} + +ConnectSignalAction::ConnectSignalAction() + : ModelNodeContextMenuAction("ConnectSignalEditor", + QObject::tr("Connect Signal to Event"), + assignEventListIcon(), + ComponentCoreConstants::eventListCategory, + QKeySequence(), + 210, + &handleAssignEventActionOperation) +{} + +ModelNodeContextMenuAction::TargetView ConnectSignalAction::targetView() const +{ + return TargetView::ConnectionEditor; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistactions.h b/src/plugins/qmldesigner/components/eventlist/eventlistactions.h new file mode 100644 index 00000000000..150abfc0663 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistactions.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include + +namespace QmlDesigner { + +class EventListAction : public ModelNodeAction +{ +public: + EventListAction(); +}; + +class AssignEventEditorAction : public ModelNodeAction +{ +public: + AssignEventEditorAction(); +}; + +class ConnectSignalAction : public ModelNodeContextMenuAction +{ +public: + ConnectSignalAction(); + TargetView targetView() const override; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.cpp new file mode 100644 index 00000000000..0a3a4f4b51d --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "eventlistdelegate.h" +#include "eventlist.h" +#include "eventlistview.h" +#include "nodelistview.h" +#include "qnamespace.h" +#include "shortcutwidget.h" +#include "eventlistutils.h" + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +EventListDelegate::EventListDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{} + +QWidget *EventListDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.column() == EventListModel::shortcutColumn) { + auto *editor = new ShortcutWidget(parent); + connect(editor, &ShortcutWidget::done, this, &EventListDelegate::commitAndClose); + connect(editor, &ShortcutWidget::cancel, this, &EventListDelegate::close); + return editor; + } else if (index.column() == EventListModel::connectColumn) { + return nullptr; + } + return QStyledItemDelegate::createEditor(parent, option, index); +} + +void EventListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (index.column() == EventListModel::idColumn) { + if (auto *edit = qobject_cast(editor)) { + QString name = edit->text(); + QString unique = uniqueName(model, name); + if (name != unique) { + name = unique; + edit->setText(unique); + } + emit eventIdChanged(model->data(index, Qt::DisplayRole).toString(), name); + } + + } else if (index.column() == EventListModel::shortcutColumn) { + if (auto *edit = qobject_cast(editor)) { + auto idIndex = model->index(index.row(), EventListModel::idColumn, index.parent()); + if (idIndex.isValid()) { + emit shortcutChanged(model->data(idIndex, Qt::DisplayRole).toString(), edit->text()); + model->setData(index, edit->text(), Qt::DisplayRole); + return; + } + } + } else if (index.column() == EventListModel::descriptionColumn) { + if (auto *edit = qobject_cast(editor)) { + auto idIndex = model->index(index.row(), EventListModel::idColumn, index.parent()); + if (idIndex.isValid()) { + auto id = model->data(idIndex, Qt::DisplayRole).toString(); + emit descriptionChanged(id, edit->text()); + } + } + } + QStyledItemDelegate::setModelData(editor, model, index); +} + +bool EventListDelegate::hasConnectionColumn(QObject *parent) +{ + if (auto *table = qobject_cast(parent)) + return !table->isColumnHidden(EventListModel::connectColumn); + + return false; +} + +QRect EventListDelegate::connectButtonRect(const QStyleOptionViewItem &option) +{ + return option.rect.adjusted(3, 3, -3, -3); +} + +void EventListDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (hasConnectionColumn(parent())) { + bool connected = index.data(EventListModel::connectedRole).toBool(); + if (connected) { + QStyleOptionViewItem opt(option); + opt.state = QStyle::State_Selected; + QStyledItemDelegate::paint(painter, opt, index); + + if (index.column() != EventListModel::connectColumn) + return; + } + + if (index.column() == EventListModel::connectColumn) { + 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 EventListDelegate::editorEvent(QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex &index) +{ + if (index.column() == EventListModel::connectColumn) { + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + if (connectButtonRect(option).contains(mouseEvent->pos())) { + if (QModelIndex sib = index.siblingAtColumn(EventListModel::idColumn); sib.isValid()) { + auto id = sib.data().toString(); + bool connected = index.data(EventListModel::connectedRole).toBool(); + for (int c = 0; c < model->columnCount(); ++c) { + auto id = model->index(index.row(), c, index.parent()); + model->setData(id, !connected, EventListModel::connectedRole); + } + emit connectClicked(id, !connected); + return true; + } + } + } + } + return QStyledItemDelegate::editorEvent(event, model, option, index); +} + +bool EventListDelegate::eventFilter(QObject *editor, QEvent *event) +{ + if (auto *edit = qobject_cast(editor)) { + if (event->type() == QEvent::KeyPress) { + edit->recordKeysequence(static_cast(event)); + return true; + } + + if (event->type() == QEvent::FocusOut) { + if (!edit->containsFocus()) + edit->reset(); + } + } else { + if (event->type() == QEvent::KeyPress) { + int key = static_cast(event)->key(); + if (key == Qt::Key_Tab || key == Qt::Key_Backtab) + return false; + } + } + + return QStyledItemDelegate::eventFilter(editor, event); +} + +QSize EventListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (index.column() == EventListModel::connectColumn) { + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.rwidth() = 20; + return size; + } + return QStyledItemDelegate::sizeHint(option, index); +} + +void EventListDelegate::commitAndClose() +{ + if (auto *editor = qobject_cast(sender())) { + emit commitData(editor); + emit closeEditor(editor); + } +} + +void EventListDelegate::close() +{ + if (auto *editor = qobject_cast(sender())) + emit closeEditor(editor); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.h b/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.h new file mode 100644 index 00000000000..49aa91f363e --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistdelegate.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#pragma once + +#include + +namespace QmlDesigner { + +class EventListDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +signals: + void eventIdChanged(const QString &from, const QString &to) const; + void shortcutChanged(const QString &from, const QString &to) const; + void descriptionChanged(const QString &id, const QString &text) const; + void connectClicked(const QString &id, bool connected) const; + +public: + EventListDelegate(QObject *parent = nullptr); + + QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + void setModelData(QWidget *editor, QAbstractItemModel *model, 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; + +protected: + bool eventFilter(QObject *editor, QEvent *event) override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + void close(); + void commitAndClose(); + + static bool hasConnectionColumn(QObject *parent); + static QRect connectButtonRect(const QStyleOptionViewItem &option); +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistdialog.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistdialog.cpp new file mode 100644 index 00000000000..2b8b5f0cc3b --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistdialog.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "eventlistdialog.h" +#include "eventlist.h" +#include "eventlistdelegate.h" +#include "eventlistview.h" +#include "filterlinewidget.h" +#include "nodelistview.h" +#include "qmldesignerplugin.h" +#include "eventlistutils.h" +#include "utils/utilsicons.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +EventListDialog::EventListDialog(QWidget *parent) + : QDialog(parent) + , m_delegate(new EventListDelegate) + , m_modifier(nullptr) + , m_rewriter(nullptr) + , m_table(new QTableView) + , m_addAction(nullptr) + , m_removeAction(nullptr) + , m_textEdit(new QPlainTextEdit) +{ + setModal(true); + setWindowFlag(Qt::Tool, true); + + m_modifier = new NotIndentingTextEditModifier(m_textEdit); + m_textEdit->hide(); + + m_table->installEventFilter(new TabWalker(this)); + m_table->setItemDelegate(m_delegate); + m_table->setModel(new QSortFilterProxyModel); + m_table->setFocusPolicy(Qt::NoFocus); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->horizontalHeader()->setStretchLastSection(true); + m_table->resizeColumnsToContents(); + + auto *toolbar = new QToolBar; + m_addAction = toolbar->addAction(Utils::Icons::PLUS_TOOLBAR.icon(), tr("Add Event")); + m_removeAction = toolbar->addAction(Utils::Icons::MINUS.icon(), tr("Remove Selected Events")); + + auto *filterWidget = new FilterLineWidget; + toolbar->addWidget(filterWidget); + + auto *tableLayout = new QVBoxLayout; + tableLayout->setSpacing(0); + tableLayout->addWidget(toolbar); + tableLayout->addWidget(m_table); + + auto *box = new QHBoxLayout; + box->addLayout(tableLayout); + setLayout(box); + + connect(filterWidget, &FilterLineWidget::filterChanged, [this](const QString &str) { + if (auto *fm = qobject_cast(m_table->model())) + fm->setFilterFixedString(str); + }); +} + +void EventListDialog::initialize(EventList &events) +{ + m_textEdit->setPlainText(events.read()); + + if (!m_rewriter) { + Model *model = events.model(); + m_modifier->setParent(model); + + m_rewriter = new RewriterView(QmlDesigner::RewriterView::Validate, model); + m_rewriter->setTextModifier(m_modifier); + m_rewriter->setCheckSemanticErrors(false); + model->attachView(m_rewriter); + + if (auto *fm = qobject_cast(m_table->model())) { + fm->setSourceModel(events.view()->eventListModel()); + } + + connect(m_addAction, &QAction::triggered, [this, &events]() { + Event event; + event.eventId = uniqueName(events.view()->eventListModel(), "event"); + events.view()->addEvent(event); + events.write(m_textEdit->toPlainText()); + }); + + connect(m_removeAction, &QAction::triggered, [this, &events]() { + for (auto index : m_table->selectionModel()->selectedRows()) { + QString eventId = index.data(Qt::DisplayRole).toString(); + events.view()->removeEvent(eventId); + } + events.write(m_textEdit->toPlainText()); + }); + + connect(m_delegate, + &EventListDelegate::eventIdChanged, + [this, &events](const QString &oldId, const QString &newId) { + events.view()->renameEvent(oldId, newId); + events.write(m_textEdit->toPlainText()); + }); + + connect(m_delegate, + &EventListDelegate::shortcutChanged, + [this, &events](const QString &id, const QString &text) { + events.view()->setShortcut(id, text); + events.write(m_textEdit->toPlainText()); + }); + + connect(m_delegate, + &EventListDelegate::descriptionChanged, + [this, &events](const QString &id, const QString &text) { + events.view()->setDescription(id, text); + events.write(m_textEdit->toPlainText()); + }); + } + + m_table->setColumnHidden(EventListModel::connectColumn, true); +} + +void EventListDialog::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + if (auto *view = EventList::nodeListView()) + view->reset(); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistdialog.h b/src/plugins/qmldesigner/components/eventlist/eventlistdialog.h new file mode 100644 index 00000000000..9203bc2a6d6 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistdialog.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include + +QT_FORWARD_DECLARE_CLASS(QPlainTextEdit) +QT_FORWARD_DECLARE_CLASS(QTableView) + +namespace QmlDesigner { + +class EventList; +class EventListDelegate; +class RewriterView; +class NotIndentingTextEditModifier; + +class EventListDialog : public QDialog +{ + Q_OBJECT + +public: + EventListDialog(QWidget *parent = nullptr); + void initialize(EventList &events); + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + EventListDelegate *m_delegate; + NotIndentingTextEditModifier *m_modifier; + RewriterView *m_rewriter; + QTableView *m_table; + QAction *m_addAction; + QAction *m_removeAction; + QPlainTextEdit *m_textEdit; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistplugin.metainfo b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.metainfo new file mode 100644 index 00000000000..257d0f10889 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.metainfo @@ -0,0 +1 @@ +MetaInfo {} diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistplugin.pri b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.pri new file mode 100644 index 00000000000..b2f58ff10a8 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.pri @@ -0,0 +1,35 @@ +QT *= qml quick core + +VPATH += $$PWD + +SOURCES += $$PWD/eventlist.cpp \ + $$PWD/eventlistview.cpp \ + $$PWD/eventlistpluginview.cpp \ + $$PWD/eventlistactions.cpp \ + $$PWD/eventlistdialog.cpp \ + $$PWD/eventlistdelegate.cpp \ + $$PWD/filterlinewidget.cpp \ + $$PWD/nodelistview.cpp \ + $$PWD/nodelistdelegate.cpp \ + $$PWD/nodeselectionmodel.cpp \ + $$PWD/assigneventdialog.cpp \ + $$PWD/shortcutwidget.cpp \ + $$PWD/connectsignaldialog.cpp \ + $$PWD/eventlistutils.cpp + +HEADERS += $$PWD/eventlist.h \ + $$PWD/eventlistpluginview.h \ + $$PWD/eventlistview.h \ + $$PWD/eventlistactions.h \ + $$PWD/eventlistdialog.h \ + $$PWD/eventlistdelegate.h \ + $$PWD/filterlinewidget.h \ + $$PWD/nodelistview.h \ + $$PWD/nodelistdelegate.h \ + $$PWD/nodeselectionmodel.h \ + $$PWD/assigneventdialog.h \ + $$PWD/shortcutwidget.h \ + $$PWD/connectsignaldialog.h \ + $$PWD/eventlistutils.h + +RESOURCES += $$PWD/eventlistplugin.qrc diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistplugin.qrc b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.qrc new file mode 100644 index 00000000000..72ce2c0c390 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistplugin.qrc @@ -0,0 +1,5 @@ + + + eventlistplugin.metainfo + + diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp new file mode 100644 index 00000000000..41d100888e6 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "eventlistpluginview.h" +#include "assigneventdialog.h" +#include "connectsignaldialog.h" +#include "eventlistactions.h" +#include "eventlistdialog.h" + +#include "signalhandlerproperty.h" +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +SignalHandlerProperty signalPropertyFromAction(ActionInterface *interface) +{ + if (auto data = interface->action()->data(); data.isValid()) { + QMap mapping = data.value>(); + ModelNode node = EventList::modelNode(mapping["ModelNode"].toString()); + PropertyName signal = mapping["Signal"].toString().toUtf8(); + + for (auto &&child : node.directSubModelNodes()) { + if (auto prop = child.signalHandlerProperty(signal); prop.exists()) + return prop; + } + } + return SignalHandlerProperty(); +} + +EventListPluginView::EventListPluginView(QObject* parent) + : AbstractView(parent) + , m_eventlist() + , m_eventListDialog(nullptr) + , m_assigner(nullptr) + , m_signalConnector(nullptr) +{ } + +void EventListPluginView::registerActions() +{ + DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->designerActionManager(); + + designerActionManager.addDesignerAction(new ActionGroup(tr("Event List"), + ComponentCoreConstants::eventListCategory, + ComponentCoreConstants::priorityEventListCategory, + &SelectionContextFunctors::always, + &SelectionContextFunctors::always)); + auto eventListAction = new EventListAction(); + connect(eventListAction->defaultAction(), &QAction::triggered, [this]() { + if (!m_eventListDialog) + m_eventListDialog = new EventListDialog(Core::ICore::dialogParent()); + + m_eventlist.initialize(this); + m_eventListDialog->initialize(m_eventlist); + m_eventListDialog->show(); + }); + designerActionManager.addDesignerAction(eventListAction); + + auto assignEventAction = new AssignEventEditorAction(); + connect(assignEventAction->defaultAction(), &QAction::triggered, [this]() { + if (!m_assigner) + m_assigner = new AssignEventDialog(Core::ICore::dialogParent()); + if (!m_eventListDialog) + m_eventListDialog = new EventListDialog(Core::ICore::dialogParent()); + + m_eventlist.initialize(this); + m_eventListDialog->initialize(m_eventlist); + m_assigner->initialize(m_eventlist); + m_assigner->show(); + m_assigner->postShow(); + }); + designerActionManager.addDesignerAction(assignEventAction); + + auto *connectSignalAction = new ConnectSignalAction(); + + connect(connectSignalAction->defaultAction(), &QAction::triggered, [this, connectSignalAction]() { + if (!m_signalConnector) + m_signalConnector = new ConnectSignalDialog(Core::ICore::dialogParent()); + + if (!m_eventListDialog) + m_eventListDialog = new EventListDialog(Core::ICore::dialogParent()); + + m_eventlist.initialize(this); + m_eventListDialog->initialize(m_eventlist); + + if (auto signal = signalPropertyFromAction(connectSignalAction); signal.isValid()) { + m_signalConnector->initialize(m_eventlist, signal); + m_signalConnector->show(); + } + }); + designerActionManager.addDesignerAction(connectSignalAction); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.h b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.h new file mode 100644 index 00000000000..be59646668d --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistpluginview.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include "eventlist.h" +#include + +namespace QmlDesigner { + +class EventListDialog; +class AssignEventDialog; +class ConnectSignalDialog; + +class EventListPluginView : public AbstractView +{ + Q_OBJECT + +public: + EventListPluginView(QObject* parent = nullptr); + ~EventListPluginView() override = default; + + void registerActions(); + +private: + EventList m_eventlist; + EventListDialog *m_eventListDialog = nullptr; + AssignEventDialog *m_assigner = nullptr; + ConnectSignalDialog *m_signalConnector = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistutils.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistutils.cpp new file mode 100644 index 00000000000..2bf5aa6be90 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistutils.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#include "eventlistutils.h" + +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +TabWalker::TabWalker(QObject *parent) + : QObject(parent) +{} + +bool TabWalker::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + auto *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) { + keyEvent->accept(); + int mapped = keyEvent->key() == Qt::Key_Tab ? Qt::Key_Down : Qt::Key_Up; + int modifiers = keyEvent->nativeModifiers() & (~Qt::ShiftModifier); + QApplication::postEvent(obj, + new QKeyEvent(QEvent::KeyPress, + mapped, + static_cast(modifiers), + keyEvent->nativeScanCode(), + keyEvent->nativeVirtualKey(), + keyEvent->nativeModifiers(), + keyEvent->text(), + keyEvent->isAutoRepeat(), + keyEvent->count())); + return true; + } + } + return QObject::eventFilter(obj, event); +} + +QStandardItemModel *sourceModel(QAbstractItemModel *model) +{ + if (auto *proxy = qobject_cast(model)) + return sourceModel(proxy->sourceModel()); + + return qobject_cast(model); +} + +QString uniqueName(QAbstractItemModel *model, const QString &base) +{ + if (auto *m = sourceModel(model)) { + QList items = m->findItems(base); + if (items.empty()) + return base; + + int idx = 0; + while (true) { + QString tmp = base + QString::number(idx++); + items = m->findItems(tmp); + if (items.empty()) + return tmp; + } + } + + return QString(); +} + +std::string toString(AbstractView::PropertyChangeFlags flags) +{ + if (flags == 0) + return std::string("NoAdditionalChanges"); + + std::string out; + if ((flags & AbstractView::NoAdditionalChanges) != 0) + out += "NoAdditionalChanges "; + + if ((flags & AbstractView::PropertiesAdded) != 0) { + if (!out.empty()) + out += "| "; + + out += "PropertiesAdded "; + } + + if ((flags & AbstractView::EmptyPropertiesRemoved) != 0) { + if (!out.empty()) + out += "| "; + + out += "EmptyPropertiesRemoved "; + } + return out; +} + +void polishPalette(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); +} + +void printPropertyType(const ModelNode &node, const PropertyName &name) +{ + std::string sname = name.toStdString(); + + auto prop = node.property(name); + if (prop.isNodeProperty()) + printf("Property %s is a node-property\n", sname.c_str()); + + if (prop.isVariantProperty()) + printf("Property %s is a variant-property\n", sname.c_str()); + + if (prop.isNodeListProperty()) + printf("Property %s is a node-list-property\n", sname.c_str()); + + if (prop.isNodeAbstractProperty()) + printf("Property %s is a node-abstract-property\n", sname.c_str()); + + if (prop.isBindingProperty()) + printf("Property %s is a binding-property\n", sname.c_str()); + + if (prop.isSignalHandlerProperty()) + printf("Property %s is a signal-handler-property\n", sname.c_str()); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistutils.h b/src/plugins/qmldesigner/components/eventlist/eventlistutils.h new file mode 100644 index 00000000000..7455456760e --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistutils.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include "abstractview.h" +#include + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) +QT_FORWARD_DECLARE_CLASS(QTableView) + +namespace QmlDesigner { + +class TabWalker : public QObject +{ + Q_OBJECT + +public: + TabWalker(QObject *parent = nullptr); + bool eventFilter(QObject *obj, QEvent *event) override; +}; + +QString uniqueName(QAbstractItemModel *model, const QString &base); + +std::string toString(AbstractView::PropertyChangeFlags flags); + +void polishPalette(QTableView *view, const QColor &selectionColor); + +void printPropertyType(const ModelNode &node, const PropertyName &name); + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp new file mode 100644 index 00000000000..c27a32de7aa --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistview.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "eventlistview.h" + +#include "assigneventdialog.h" +#include "connectsignaldialog.h" +#include "eventlistactions.h" +#include "eventlistdialog.h" +#include "nodelistproperty.h" +#include "nodeproperty.h" +#include "variantproperty.h" +#include +#include + +#include +#include + +namespace QmlDesigner { + +EventListModel::EventListModel(QObject *parent) + : QStandardItemModel(0, 4, parent) +{ + setHeaderData(idColumn, Qt::Horizontal, tr("Event ID")); + setHeaderData(shortcutColumn, Qt::Horizontal, tr("Shortcut")); + setHeaderData(descriptionColumn, Qt::Horizontal, tr("Description")); + setHeaderData(connectColumn, Qt::Horizontal, tr("")); +} + +QStringList EventListModel::connectedEvents() const +{ + QStringList out; + for (int row = 0; row < rowCount(); ++row) { + if (auto idx = index(row, EventListModel::idColumn); idx.isValid()) { + if (idx.data(EventListModel::connectedRole).toBool()) + out << idx.data().toString(); + } + } + return out; +} + +QStringList EventListModel::connectEvents(const QStringList &eventIds) +{ + auto out = eventIds; + for (int row = 0; row < rowCount(); ++row) { + auto nameIndex = index(row, EventListModel::idColumn); + bool connected = out.removeOne(nameIndex.data().toString()); + for (int col = 0; col < columnCount(); col++) + setData(index(row, col), connected, EventListModel::connectedRole); + } + return out; +} + + +EventListView::EventListView(QObject *parent) + : AbstractView(parent) + , m_eventlist() + , m_model(new EventListModel(this)) +{} + +EventListView::~EventListView() {} + +void EventListView::nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) +{ + AbstractView::nodeRemoved(removedNode, parentProperty, propertyChange); + reset(); +} + +void EventListView::nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + PropertyChangeFlags propertyChange) +{ + AbstractView::nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange); + reset(); +} + +EventListModel *EventListView::eventListModel() const +{ + return m_model; +} + +void EventListView::addEvent(const Event &event) +{ + executeInTransaction("EventListView::addEvent", [=]() { + + QByteArray unqualifiedTypeName = "ListElement"; + auto metaInfo = model()->metaInfo(unqualifiedTypeName); + + QByteArray fullTypeName = metaInfo.typeName(); + int minorVersion = metaInfo.minorVersion(); + int majorVersion = metaInfo.majorVersion(); + + ModelNode eventNode = createModelNode(fullTypeName, majorVersion, minorVersion); + eventNode.variantProperty("eventId").setValue(event.eventId); + + if (!event.shortcut.isEmpty()) + eventNode.variantProperty("shortcut").setValue(event.shortcut); + + if (!event.description.isEmpty()) + eventNode.variantProperty("eventDescription").setValue(event.description); + + rootModelNode().defaultNodeListProperty().reparentHere(eventNode); + }); +} + +void EventListView::removeEvent(const QString &eventId) +{ + executeInTransaction("EventListView::removeEvent", [=]() { + for (auto node : rootModelNode().defaultNodeListProperty().toModelNodeList()) { + if (node.variantProperty("eventId").value().toString() == eventId) { + node.destroy(); + return; + } + } + }); +} + +void EventListView::renameEvent(const QString &oldId, const QString &newId) +{ + executeInTransaction("EventListView::renameEvent", [=]() { + for (auto node : rootModelNode().defaultNodeListProperty().toModelNodeList()) { + if (node.variantProperty("eventId").value().toString() == oldId) { + node.variantProperty("eventId").setValue(newId); + return; + } + } + }); +} + +void EventListView::setShortcut(const QString &id, const QString &text) +{ + executeInTransaction("EventListView::setShortcut", [=]() { + for (auto node : rootModelNode().defaultNodeListProperty().toModelNodeList()) { + if (node.variantProperty("eventId").value().toString() == id) { + node.variantProperty("shortcut").setValue(text); + return; + } + } + }); +} + +void EventListView::setDescription(const QString &id, const QString &text) +{ + executeInTransaction("EventListView::setDescription", [=]() { + for (auto node : rootModelNode().defaultNodeListProperty().toModelNodeList()) { + if (node.variantProperty("eventId").value().toString() == id) { + node.variantProperty("eventDescription").setValue(text); + return; + } + } + }); +} + +void EventListView::reset() +{ + auto setData = [this](int row, int column, const QVariant &data) { + QModelIndex idx = m_model->index(row, column, QModelIndex()); + m_model->setData(idx, data); + }; + + if (rootModelNode().isValid()) { + m_model->removeRows(0, m_model->rowCount(QModelIndex()), QModelIndex()); + for (auto itemNode : rootModelNode().directSubModelNodes()) { + int row = m_model->rowCount(); + if (m_model->insertRows(row, 1, QModelIndex())) { + QVariant eventId = itemNode.variantProperty("eventId").value(); + QVariant eventShortcut = itemNode.variantProperty("shortcut").value(); + QVariant eventDescription = itemNode.variantProperty("eventDescription").value(); + + setData(row, EventListModel::idColumn, eventId); + setData(row, EventListModel::shortcutColumn, eventShortcut); + setData(row, EventListModel::descriptionColumn, eventDescription); + } + } + } +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistview.h b/src/plugins/qmldesigner/components/eventlist/eventlistview.h new file mode 100644 index 00000000000..bf3e8ebffbb --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/eventlistview.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include "eventlist.h" + +#include +#include + +namespace QmlDesigner { + +struct Event +{ + QString eventId; + QString shortcut; + QString description; +}; + +class EventListModel : public QStandardItemModel +{ + Q_OBJECT + +public: + enum Columns { + idColumn = 0, + descriptionColumn = 1, + shortcutColumn = 2, + connectColumn = 3 + }; + + enum Roles : unsigned int { + connectedRole = Qt::UserRole + 1, + }; + + EventListModel(QObject *parent = nullptr); + + QStringList connectedEvents() const; + + QStringList connectEvents(const QStringList &eventIds); +}; + + +class EventListDialog; +class AssignEventDialog; +class ConnectSignalDialog; + +class EventListView : public AbstractView +{ + Q_OBJECT + +public: + explicit EventListView(QObject *parent = nullptr); + ~EventListView() override; + + void nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override; + + void nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + PropertyChangeFlags propertyChange) override; + + EventListModel *eventListModel() const; + + void addEvent(const Event &event); + void removeEvent(const QString &name); + void renameEvent(const QString &oldId, const QString &newId); + void setShortcut(const QString &id, const QString &text); + void setDescription(const QString &id, const QString &text); + +private: + void reset(); + + EventList m_eventlist; + EventListModel *m_model; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/filterlinewidget.cpp b/src/plugins/qmldesigner/components/eventlist/filterlinewidget.cpp new file mode 100644 index 00000000000..6bab3cb1f88 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/filterlinewidget.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "filterlinewidget.h" + +#include +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +FilterLineWidget::FilterLineWidget(QWidget *parent) + : QWidget(parent) + , m_edit(new Utils::FancyLineEdit()) +{ + 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(18, 18))); + label->setAlignment(Qt::AlignCenter); + + m_edit->setPlaceholderText(QObject::tr("", "Library search input hint text")); + m_edit->setDragEnabled(false); + m_edit->setMinimumWidth(75); + m_edit->setTextMargins(0, 0, 20, 0); + m_edit->setFiltering(true); + + auto *box = new QHBoxLayout; + box->addWidget(label); + box->addWidget(m_edit); + setLayout(box); + + connect(m_edit, &Utils::FancyLineEdit::filterChanged, this, &FilterLineWidget::filterChanged); +} + +void FilterLineWidget::clear() +{ + m_edit->clear(); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/filterlinewidget.h b/src/plugins/qmldesigner/components/eventlist/filterlinewidget.h new file mode 100644 index 00000000000..4c856e766aa --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/filterlinewidget.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#pragma once + +#include + +namespace Utils { +class FancyLineEdit; +} + +namespace QmlDesigner { + +class FilterLineWidget : public QWidget +{ + Q_OBJECT + +signals: + void filterChanged(const QString &filter); + +public: + FilterLineWidget(QWidget *parent = nullptr); + + void clear(); + +private: + Utils::FancyLineEdit *m_edit; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.cpp b/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.cpp new file mode 100644 index 00000000000..28690d40b9f --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "nodelistdelegate.h" +#include "eventlist.h" +#include "eventlistview.h" +#include "nodelistview.h" +#include "qnamespace.h" +#include "shortcutwidget.h" +#include "eventlistutils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +NodeListDelegate::NodeListDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{} + +void NodeListDelegate::setModelData(QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (index.column() == NodeListModel::idColumn) { + if (auto *edit = qobject_cast(editor)) { + QVariant iidVariant = index.data(NodeListModel::internalIdRole); + if (iidVariant.isValid()) { + QString verifiedId = EventList::setNodeId(iidVariant.toInt(), edit->text()); + if (!verifiedId.isNull()) + edit->setText(verifiedId); + else + edit->setText(""); + } + } + } + QStyledItemDelegate::setModelData(editor, model, index); +} + +bool NodeListDelegate::eventFilter(QObject *editor, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + int key = static_cast(event)->key(); + if (key == Qt::Key_Tab || key == Qt::Key_Backtab) + return false; + } + return QStyledItemDelegate::eventFilter(editor, event); +} + +void NodeListDelegate::commitAndClose() +{ + if (auto *editor = qobject_cast(sender())) { + emit commitData(editor); + emit closeEditor(editor); + } +} + +void NodeListDelegate::close() +{ + if (auto *editor = qobject_cast(sender())) + emit closeEditor(editor); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.h b/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.h new file mode 100644 index 00000000000..186fc2dea0d --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodelistdelegate.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#pragma once + +#include + +namespace QmlDesigner { + +class NodeListDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + NodeListDelegate(QObject *parent = nullptr); + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + +protected: + bool eventFilter(QObject *editor, QEvent *event) override; + +private: + void close(); + void commitAndClose(); +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp b/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp new file mode 100644 index 00000000000..4171a5335b2 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "nodelistview.h" +#include "eventlist.h" + +#include "bindingproperty.h" +#include "nodemetainfo.h" +#include "utils/qtcassert.h" +#include "variantproperty.h" + +#include + +#include +#include + +namespace QmlDesigner { + +QStringList eventIdsFromVariant(const QVariant &val) +{ + QStringList eventIds = val.toString().split(",", Qt::SkipEmptyParts); + for (QString &str : eventIds) + str = str.trimmed(); + return eventIds; +} + +QVariant eventIdsToVariant(const QStringList &eventIds) +{ + if (eventIds.empty()) + return QVariant(); + + return QVariant(eventIds.join(", ")); +} + +NodeListModel::NodeListModel(QObject *parent) + : QStandardItemModel(0, 4, parent) +{ + setHeaderData(idColumn, Qt::Horizontal, tr("Id")); + setHeaderData(typeColumn, Qt::Horizontal, tr("Type")); + setHeaderData(fromColumn, Qt::Horizontal, tr("From")); + setHeaderData(toColumn, Qt::Horizontal, tr("To")); + setSortRole(internalIdRole); +} + + +NodeListView::NodeListView(AbstractView *parent) + : AbstractView(parent) + , m_itemModel(new NodeListModel(this)) +{ + parent->model()->attachView(this); + reset(); +} + +NodeListView::~NodeListView() {} + +QStandardItemModel *NodeListView::itemModel() const +{ + return m_itemModel; +} + +int NodeListView::currentNode() const +{ + ModelNode node = firstSelectedModelNode(); + if (node.isValid()) + return node.internalId(); + + return -1; +} + +void NodeListView::selectNode(int internalId) +{ + ModelNode node = modelNodeForInternalId(internalId); + if (node.isValid()) { + clearSelectedModelNodes(); + selectModelNode(node); + } +} + + +QModelIndex indexByInternalId(QAbstractItemModel *model, int id) +{ + constexpr int count = 1; + auto ids = model->match(model->index(0, NodeListModel::idColumn), + NodeListModel::internalIdRole, + id, + count, + Qt::MatchExactly); + if (ids.size() == count) + return ids.first(); + + return QModelIndex(); +} + +bool NodeListView::addEventId(int nodeId, const QString &event) +{ + if (auto node = compatibleModelNode(nodeId); node.isValid()) { + QStringList events; + if (node.hasProperty("eventIds")) + events = eventIdsFromVariant(node.variantProperty("eventIds").value()); + events.push_back(event); + + return setEventIds(node, events); + } + return false; +} + +bool NodeListView::removeEventIds(int nodeId, const QStringList &eventIds) +{ + if (auto node = compatibleModelNode(nodeId); node.isValid()) { + QStringList events = eventIdsFromVariant(node.variantProperty("eventIds").value()); + for (auto &&remove : eventIds) + Q_UNUSED(events.removeOne(remove)); + + return setEventIds(node, events); + } + return false; +} + +QString NodeListView::setNodeId(int internalId, const QString &id) +{ + ModelNode node = modelNodeForInternalId(internalId); + if (node.isValid()) { + QString newId = generateNewId(id); + node.setIdWithRefactoring(newId); + return newId; + } + + return QString(); +} + +bool supported(const ModelNode &node) +{ + if (!node.isValid()) + return false; + + const NodeMetaInfo metaInfo = node.metaInfo(); + if (!metaInfo.isValid()) + return false; + + return metaInfo.hasProperty("eventIds"); +} + +static QIcon flowTypeIconFromFont(const TypeName &type) +{ + QString unicode = Theme::getIconUnicode(Theme::Icon::edit); + const QString fontName = "qtds_propertyIconFont.ttf"; + if (type == "FlowView.FlowTransition") + unicode = Theme::getIconUnicode(Theme::Icon::flowTransition); + else if (type == "FlowView.FlowActionArea") + unicode = Theme::getIconUnicode(Theme::Icon::flowAction); + else if (type == "FlowView.FlowWildcard") + unicode = Theme::getIconUnicode(Theme::Icon::wildcard); + + return Utils::StyleHelper::getIconFromIconFont(fontName, unicode, 28, 28); +} + +void NodeListView::reset() +{ + auto setData = [this](int row, int column, const QVariant &data, int role = Qt::EditRole) { + QModelIndex idx = m_itemModel->index(row, column, QModelIndex()); + m_itemModel->setData(idx, data, role); + }; + + m_itemModel->removeRows(0, m_itemModel->rowCount(QModelIndex()), QModelIndex()); + + for (auto &&node : allModelNodes()) { + if (supported(node)) { + int row = m_itemModel->rowCount(); + if (m_itemModel->insertRows(row, 1, QModelIndex())) { + int iid = node.internalId(); + auto eventIds = eventIdsFromVariant(node.variantProperty("eventIds").value()); + + setData(row, NodeListModel::idColumn, node.id()); + setData(row, + NodeListModel::idColumn, + flowTypeIconFromFont(node.type()), + Qt::DecorationRole); + setData(row, NodeListModel::idColumn, iid, NodeListModel::internalIdRole); + setData(row, NodeListModel::idColumn, eventIds, NodeListModel::eventIdsRole); + + setData(row, NodeListModel::typeColumn, node.type()); + setData(row, NodeListModel::fromColumn, node.bindingProperty("from").expression()); + setData(row, NodeListModel::toColumn, node.bindingProperty("to").expression()); + } + } + } + m_itemModel->sort(0); +} + +ModelNode NodeListView::compatibleModelNode(int nodeId) +{ + if (auto node = modelNodeForInternalId(nodeId); node.isValid()) { + QTC_ASSERT(node.metaInfo().isValid(), return ModelNode()); + QTC_ASSERT(node.metaInfo().hasProperty("eventIds"), return ModelNode()); + return node; + } + return ModelNode(); +} + +bool setEventIdsInModelNode(AbstractView *view, const ModelNode &node, const QStringList &events) +{ + if (events.empty()) { + if (node.hasProperty("eventIds")) { + return view->executeInTransaction("NodeListView::setEventIds", + [=]() { node.removeProperty("eventIds"); }); + } + } else { + QStringList copy(events); + Q_UNUSED(copy.removeDuplicates()); + QString value = events.join(", "); + return view->executeInTransaction("NodeListView::setEventIds", [=]() { + node.variantProperty("eventIds").setValue(value); + }); + } + return false; +} + +bool NodeListView::setEventIds(const ModelNode &node, const QStringList &events) +{ + bool result = setEventIdsInModelNode(this, node, events); + auto modelIndex = indexByInternalId(m_itemModel, node.internalId()); + if (modelIndex.isValid() && result) { + m_itemModel->setData(modelIndex, events, NodeListModel::eventIdsRole); + return true; + } + return false; +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodelistview.h b/src/plugins/qmldesigner/components/eventlist/nodelistview.h new file mode 100644 index 00000000000..99ba5e3edb7 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodelistview.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class NodeListModel : public QStandardItemModel +{ + Q_OBJECT + +public: + enum Columns : unsigned int { + idColumn = 0, + typeColumn = 1, + fromColumn = 2, + toColumn = 3 + }; + + enum Roles : unsigned int { + internalIdRole = Qt::UserRole + 1, + eventIdsRole = Qt::UserRole + 2 + }; + + NodeListModel(QObject *parent = nullptr); +}; + + +class NodeListView : public AbstractView +{ + Q_OBJECT + +public: + explicit NodeListView(AbstractView *parent); + ~NodeListView() override; + + int currentNode() const; + QStandardItemModel *itemModel() const; + + void reset(); + void selectNode(int internalId); + bool addEventId(int nodeId, const QString &event); + bool removeEventIds(int nodeId, const QStringList &events); + QString setNodeId(int internalId, const QString &id); + +private: + ModelNode compatibleModelNode(int nodeId); + bool setEventIds(const ModelNode &node, const QStringList &events); + + QStandardItemModel *m_itemModel; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.cpp b/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.cpp new file mode 100644 index 00000000000..07f30f419b5 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#include "nodeselectionmodel.h" +#include "eventlist.h" +#include "nodelistview.h" +#include +#include + +namespace QmlDesigner { + +NodeSelectionModel::NodeSelectionModel(QAbstractItemModel *model) + : QItemSelectionModel(model) +{} + +void NodeSelectionModel::select(const QItemSelection &selection, + QItemSelectionModel::SelectionFlags command) +{ + QItemSelectionModel::select(selection, command); + + for (auto &&modelIndex : selection.indexes()) { + if (modelIndex.column() == NodeListModel::idColumn) { + int id = modelIndex.data(NodeListModel::internalIdRole).toInt(); + EventList::selectNode(id); + emit nodeSelected(modelIndex.data(NodeListModel::eventIdsRole).toStringList()); + } + } +} + +QList NodeSelectionModel::selectedNodes() const +{ + QList out; + for (auto index : selectedRows()) + out.push_back(index.data(NodeListModel::internalIdRole).toInt()); + + return out; +} + +void NodeSelectionModel::selectNode(int nodeId) +{ + if (nodeId < 0) { + clearSelection(); + return; + } + + if (auto *model = qobject_cast(this->model())) { + auto start = model->index(0, 0); + auto indexes = model->match(start, + NodeListModel::internalIdRole, + QString::number(nodeId), + 1, + Qt::MatchExactly); + + for (auto index : indexes) + QItemSelectionModel::select(index, QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect); + } +} + +void NodeSelectionModel::storeSelection() +{ + if (const auto *proxyModel = qobject_cast(model())) { + if (hasSelection()) + m_stored = proxyModel->mapSelectionToSource(selection()); + } +} + +void NodeSelectionModel::reselect() +{ + if (const auto *proxyModel = qobject_cast(model())) + select(proxyModel->mapSelectionFromSource(m_stored), + QItemSelectionModel::Select | QItemSelectionModel::Rows); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.h b/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.h new file mode 100644 index 00000000000..1b017a5d657 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/nodeselectionmodel.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#pragma once + +#include + +namespace QmlDesigner { + +class NodeSelectionModel : public QItemSelectionModel +{ + Q_OBJECT + +signals: + void nodeSelected(const QStringList &events); + +public: + NodeSelectionModel(QAbstractItemModel *model); + + void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override; + + QList selectedNodes() const; + void selectNode(int nodeId); + + void storeSelection(); + void reselect(); + +private: + QItemSelection m_stored; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/shortcutwidget.cpp b/src/plugins/qmldesigner/components/eventlist/shortcutwidget.cpp new file mode 100644 index 00000000000..58b0711703c --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/shortcutwidget.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ + +#include "shortcutwidget.h" + +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +ShortcutWidget::ShortcutWidget(QWidget *parent) + : QWidget(parent) + , m_text(new QLineEdit) + , m_button(new QPushButton("R")) + , m_key({{0, 0, 0, 0}}) + , m_keyNum(0) +{ + connect(m_button, &QPushButton::pressed, this, &ShortcutWidget::done); + + auto *box = new QHBoxLayout; + box->setContentsMargins(0, 0, 0, 0); + box->setSpacing(0); + box->addWidget(m_text); + box->addWidget(m_button); + setLayout(box); + + m_text->setReadOnly(true); + m_text->setFocusPolicy(Qt::NoFocus); +} + +bool ShortcutWidget::containsFocus() const +{ + if (auto *fw = QApplication::focusWidget()) + if (fw->parentWidget() == this) + return true; + return false; +} + +QString ShortcutWidget::text() const +{ + return m_text->text(); +} + +void ShortcutWidget::reset() +{ + emit cancel(); +} + +void ShortcutWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + int width = event->size().width(); + m_text->setFixedWidth(width * 3 / 4); + m_button->setFixedWidth(width / 4); +} + +// Forked from qt-creator/shortcutsettings.cpp +static int translateModifiers(Qt::KeyboardModifiers state, const QString &text) +{ + int result = 0; + // The shift modifier only counts when it is not used to type a symbol + // that is only reachable using the shift key anyway + if ((state & Qt::ShiftModifier) + && (text.isEmpty() || !text.at(0).isPrint() || text.at(0).isLetterOrNumber() + || text.at(0).isSpace())) + result |= Qt::SHIFT; + if (state & Qt::ControlModifier) + result |= Qt::CTRL; + if (state & Qt::MetaModifier) + result |= Qt::META; + if (state & Qt::AltModifier) + result |= Qt::ALT; + return result; +} + +void ShortcutWidget::recordKeysequence(QKeyEvent *event) +{ + int nextKey = event->key(); + if (m_keyNum > 3 || nextKey == Qt::Key_Control || nextKey == Qt::Key_Shift + || nextKey == Qt::Key_Meta || nextKey == Qt::Key_Alt) { + return; + } + + nextKey |= translateModifiers(event->modifiers(), event->text()); + m_key[m_keyNum] = nextKey; + m_keyNum++; + event->accept(); + + auto sequence = QKeySequence(m_key[0], m_key[1], m_key[2], m_key[3]); + m_text->setText(sequence.toString()); +} + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/eventlist/shortcutwidget.h b/src/plugins/qmldesigner/components/eventlist/shortcutwidget.h new file mode 100644 index 00000000000..6a4ca7bf664 --- /dev/null +++ b/src/plugins/qmldesigner/components/eventlist/shortcutwidget.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us +** +** This file is part of the Qt Event List module. +** +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +******************************************************************************/ +#pragma once + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +namespace QmlDesigner { + +class ShortcutWidget : public QWidget +{ + Q_OBJECT + +signals: + void done(); + void cancel(); + +public: + ShortcutWidget(QWidget *parent = nullptr); + bool containsFocus() const; + QString text() const; + + void reset(); + void recordKeysequence(QKeyEvent *event); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + QLineEdit *m_text; + QPushButton *m_button; + std::array m_key; + int m_keyNum = 0; +}; + +} // namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index c35539942b5..77c4715f172 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -254,6 +255,10 @@ bool QmlDesignerPlugin::delayedInitialize() auto curveEditorView = new QmlDesigner::CurveEditorView; d->viewManager.registerViewTakingOwnership(curveEditorView); + + auto eventlistView = new QmlDesigner::EventListPluginView; + d->viewManager.registerViewTakingOwnership(eventlistView); + eventlistView->registerActions(); } auto transitionEditorView = new QmlDesigner::TransitionEditorView; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index 85a7685da59..6ed8535da36 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -35,6 +35,7 @@ include(components/richtexteditor/richtexteditor.pri) include(components/transitioneditor/transitioneditor.pri) include(components/listmodeleditor/listmodeleditor.pri) include(components/previewtooltip/previewtooltipbackend.pri) +include(components/eventlist/eventlistplugin.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) !isEmpty(BUILD_PUPPET_IN_CREATOR_BINPATH) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 1e7e7fc6e41..eb91d363797 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -51,6 +51,7 @@ Project { "components/integration", "components/propertyeditor", "components/edit3d", + "components/eventlist", "components/formeditor", "components/itemlibrary", "components/navigator", @@ -841,8 +842,37 @@ Project { "curveeditor/detail/treemodel.h", "curveeditor/detail/treeview.cpp", "curveeditor/detail/treeview.h", - "curveeditor/detail/utils.cpp", - "curveeditor/detail/utils.h", + "curveeditor/detail/curveeditorutils.cpp", + "curveeditor/detail/curveeditorutils.h", + "eventlist/assigneventdialog.cpp", + "eventlist/assigneventdialog.h", + "eventlist/connectsignaldialog.cpp", + "eventlist/connectsignaldialog.h", + "eventlist/eventlist.cpp", + "eventlist/eventlist.h", + "eventlist/eventlistactions.cpp", + "eventlist/eventlistactions.h", + "eventlist/eventlistdelegate.cpp", + "eventlist/eventlistdelegate.h", + "eventlist/eventlistdialog.cpp", + "eventlist/eventlistdialog.h", + "eventlist/eventlistplugin.qrc", + "eventlist/eventlistpluginview.cpp", + "eventlist/eventlistpluginview.h", + "eventlist/eventlistutils.cpp", + "eventlist/eventlistutils.h", + "eventlist/eventlistview.cpp", + "eventlist/eventlistview.h", + "eventlist/filterlinewidget.cpp", + "eventlist/filterlinewidget.h", + "eventlist/nodelistdelegate.cpp", + "eventlist/nodelistdelegate.h", + "eventlist/nodelistview.cpp", + "eventlist/nodelistview.h", + "eventlist/nodeselectionmodel.cpp", + "eventlist/nodeselectionmodel.h", + "eventlist/shortcutwidget.cpp", + "eventlist/shortcutwidget.h", "pathtool/controlpoint.cpp", "pathtool/controlpoint.h", "pathtool/cubicsegment.cpp",