aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner
diff options
context:
space:
mode:
authorThomas Hartmann <thomas.hartmann@qt.io>2020-04-14 18:26:04 +0200
committerThomas Hartmann <thomas.hartmann@qt.io>2020-04-24 11:32:17 +0000
commit88e07d8a7a5bb38207060652e013d50f02629e5c (patch)
tree946f996a994b1e48d2354d1b48284b364fd4060e /src/plugins/qmldesigner
parent8c50ceb294dbdfd7a50e81d3d7d6277b414c16b7 (diff)
QmlDesigner: Implement transition tool
Implementing a custom tool to create transitions in flows. Change-Id: I721895c67084707bea8504c657ec9af2b5df2c22 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner')
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditor.pri6
-rw-r--r--src/plugins/qmldesigner/components/formeditor/transitiontool.cpp438
-rw-r--r--src/plugins/qmldesigner/components/formeditor/transitiontool.h98
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp2
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs2
6 files changed, 545 insertions, 2 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 42feb7c13f..f6ac481714 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -243,6 +243,7 @@ extend_qtc_plugin(QmlDesigner
snapper.cpp snapper.h
snappinglinecreator.cpp snappinglinecreator.h
toolbox.cpp toolbox.h
+ transitiontool.cpp transitiontool.h
)
extend_qtc_plugin(QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri
index 3464ba3afe..4609b277f9 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditor.pri
+++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri
@@ -36,7 +36,8 @@ SOURCES += formeditoritem.cpp \
contentnoteditableindicator.cpp \
backgroundaction.cpp \
formeditortoolbutton.cpp \
- formeditorannotationicon.cpp
+ formeditorannotationicon.cpp \
+ transitiontool.cpp
HEADERS += formeditorscene.h \
formeditorwidget.h \
@@ -75,6 +76,7 @@ HEADERS += formeditorscene.h \
contentnoteditableindicator.h \
backgroundaction.h \
formeditortoolbutton.h \
- formeditorannotationicon.h
+ formeditorannotationicon.h \
+ transitiontool.h
RESOURCES += formeditor.qrc
diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp
new file mode 100644
index 0000000000..b1cee8c443
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.cpp
@@ -0,0 +1,438 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "transitiontool.h"
+
+#include <formeditorscene.h>
+#include <formeditorview.h>
+#include <formeditorwidget.h>
+#include <itemutilfunctions.h>
+#include <formeditoritem.h>
+#include <layeritem.h>
+
+#include <resizehandleitem.h>
+
+#include <bindingproperty.h>
+#include <nodeabstractproperty.h>
+#include <nodelistproperty.h>
+#include <nodemetainfo.h>
+#include <qmlitemnode.h>
+#include <qmldesignerplugin.h>
+#include <abstractaction.h>
+#include <designeractionmanager.h>
+#include <variantproperty.h>
+#include <rewritingexception.h>
+#include <rewritertransaction.h>
+
+#include <coreplugin/icore.h>
+#include <utils/qtcassert.h>
+
+#include <QApplication>
+#include <QGraphicsSceneMouseEvent>
+#include <QAction>
+#include <QMessageBox>
+#include <QPair>
+#include <QGraphicsSceneMouseEvent>
+
+namespace QmlDesigner {
+
+static bool isTransitionSource(const ModelNode &node)
+{
+ return QmlFlowTargetNode::isFlowEditorTarget(node);
+}
+
+static bool isTransitionTarget(const QmlItemNode &node)
+{
+ return QmlFlowTargetNode::isFlowEditorTarget(node)
+ && !node.isFlowActionArea()
+ && !node.isFlowWildcard();
+}
+
+class TransitionToolAction : public AbstractAction
+{
+public:
+ TransitionToolAction(const QString &name) : AbstractAction(name) {}
+
+ QByteArray category() const override
+ {
+ return QByteArray();
+ }
+
+ QByteArray menuId() const override
+ {
+ return "TransitionTool";
+ }
+
+ int priority() const override
+ {
+ return CustomActionsPriority;
+ }
+
+ Type type() const override
+ {
+ return ContextMenuAction;
+ }
+
+protected:
+ bool isVisible(const SelectionContext &selectionContext) const override
+ {
+ if (selectionContext.scenePosition().isNull())
+ return false;
+
+ if (selectionContext.singleNodeIsSelected())
+ return isTransitionSource(selectionContext.currentSingleSelectedNode());
+
+ return false;
+ }
+
+ bool isEnabled(const SelectionContext &selectionContext) const override
+ {
+ return isVisible(selectionContext);
+ }
+};
+
+class TransitionCustomAction : public TransitionToolAction
+{
+public:
+ TransitionCustomAction(const QString &name) : TransitionToolAction(name) {}
+
+ QByteArray category() const override
+ {
+ return ComponentCoreConstants::flowCategory;
+ }
+
+ SelectionContext selectionContext() const
+ {
+ return AbstractAction::selectionContext();
+ }
+
+};
+
+static QRectF paintedBoundingRect(FormEditorItem *item)
+{
+ QRectF boundingRect = item->qmlItemNode().instanceBoundingRect();
+ if (boundingRect.width() < 4)
+ boundingRect = item->boundingRect();
+ return boundingRect;
+}
+
+static QPointF centerPoint(FormEditorItem *item)
+{
+ QRectF boundingRect = paintedBoundingRect(item);
+ return QPointF(item->scenePos().x() + boundingRect.width() / 2,
+ item->scenePos().y() + boundingRect.height() / 2);
+}
+
+void static setToBoundingRect(QGraphicsRectItem *rect, FormEditorItem *item)
+{
+ QPolygonF boundingRectInSceneSpace(item->mapToScene(paintedBoundingRect(item)));
+ rect->setRect(boundingRectInSceneSpace.boundingRect());
+}
+
+TransitionTool::TransitionTool()
+ : QObject(), AbstractCustomTool()
+{
+
+ TransitionToolAction *transitionToolAction = new TransitionToolAction(tr("Add Transition"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(transitionToolAction);
+
+ connect(transitionToolAction->action(), &QAction::triggered,
+ this, &TransitionTool::activateTool);
+
+ TransitionCustomAction *removeAction = new TransitionCustomAction(tr("Remove Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAction);
+
+ connect(removeAction->action(), &QAction::triggered,
+ this, [removeAction](){
+
+ SelectionContext context = removeAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove Transitions", [&node](){
+ if (node.isValid())
+ node.removeTransitions();
+ });
+ });
+
+ TransitionCustomAction *removeAllTransitionsAction = new TransitionCustomAction(tr("Remove All Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeAllTransitionsAction);
+
+ connect(removeAllTransitionsAction->action(), &QAction::triggered,
+ this, [removeAllTransitionsAction](){
+
+ if (QMessageBox::question(Core::ICore::dialogParent(),
+ tr("Remove All Transitions"),
+ tr("Do you really want to remove all transitions?"),
+ QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
+ return;
+
+ SelectionContext context = removeAllTransitionsAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove All Transitions", [&node](){
+ if (node.isValid() && node.flowView().isValid())
+ node.flowView().removeAllTransitions();
+ });
+ });
+
+ TransitionCustomAction *removeDanglingTransitionAction = new TransitionCustomAction(tr("Remove Dangling Transitions"));
+ QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(removeDanglingTransitionAction);
+
+ connect(removeDanglingTransitionAction->action(), &QAction::triggered,
+ this, [removeDanglingTransitionAction](){
+
+ SelectionContext context = removeDanglingTransitionAction->selectionContext();
+ QmlFlowTargetNode node = QmlFlowTargetNode(context.currentSingleSelectedNode());
+
+ context.view()->executeInTransaction("Remove Dangling Transitions", [&node](){
+ if (node.isValid() && node.flowView().isValid())
+ node.flowView().removeDanglingTransitions();
+ });
+ });
+}
+
+TransitionTool::~TransitionTool()
+{
+}
+
+void TransitionTool::clear()
+{
+ m_lineItem.reset(nullptr);
+ m_rectangleItem1.reset(nullptr);
+ m_rectangleItem2.reset(nullptr);
+
+ AbstractFormEditorTool::clear();
+}
+
+void TransitionTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ if (m_block)
+ return;
+
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ AbstractFormEditorTool::mousePressEvent(itemList, event);
+ TransitionTool::mouseMoveEvent(itemList, event);
+}
+
+void TransitionTool::mouseMoveEvent(const QList<QGraphicsItem*> & itemList,
+ QGraphicsSceneMouseEvent * event)
+{
+ if (!m_lineItem)
+ return;
+
+ QTC_ASSERT(currentFormEditorItem(), return);
+
+ const QPointF pos = centerPoint(m_formEditorItem);
+ lineItem()->setLine(pos.x(),
+ pos.y(),
+ event->scenePos().x(),
+ event->scenePos().y());
+
+ FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
+
+ if (formEditorItem
+ && formEditorItem->qmlItemNode().isValid()
+ && isTransitionTarget(formEditorItem->qmlItemNode().modelNode())) {
+ rectangleItem2()->setVisible(true);
+ setToBoundingRect(rectangleItem2(), formEditorItem);
+ } else {
+ rectangleItem2()->setVisible(false);
+ }
+}
+
+void TransitionTool::hoverMoveEvent(const QList<QGraphicsItem*> & itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ mouseMoveEvent(itemList, event);
+}
+
+void TransitionTool::keyPressEvent(QKeyEvent * /*keyEvent*/)
+{
+}
+
+void TransitionTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/)
+{
+ view()->changeToSelectionTool();
+}
+
+void TransitionTool::dragLeaveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
+{
+}
+
+void TransitionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/)
+{
+}
+
+void TransitionTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event)
+{
+ if (m_block)
+ return;
+
+ if (event->button() == Qt::LeftButton) {
+ FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
+
+ if (formEditorItem
+ && QmlFlowTargetNode(formEditorItem->qmlItemNode().modelNode()).isValid())
+ createTransition(m_formEditorItem, formEditorItem);
+ }
+
+ view()->changeToSelectionTool();
+}
+
+
+void TransitionTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
+{
+ AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event);
+}
+
+void TransitionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &)
+{
+ view()->changeCurrentToolTo(this);
+}
+
+void TransitionTool::selectedItemsChanged(const QList<FormEditorItem*> &itemList)
+{
+ if (!itemList.isEmpty()) {
+ createItems();
+
+ m_formEditorItem = itemList.first();
+ setToBoundingRect(rectangleItem1(), m_formEditorItem);
+ }
+}
+
+void TransitionTool::instancesCompleted(const QList<FormEditorItem*> & /*itemList*/)
+{
+}
+
+void TransitionTool::instancesParentChanged(const QList<FormEditorItem *> & /*itemList*/)
+{
+}
+
+void TransitionTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > & /*propertyList*/)
+{
+}
+
+void TransitionTool::formEditorItemsChanged(const QList<FormEditorItem*> & /*itemList*/)
+{
+}
+
+int TransitionTool::wantHandleItem(const ModelNode &modelNode) const
+{
+ if (isTransitionSource(modelNode))
+ return 10;
+
+ return 0;
+}
+
+QString TransitionTool::name() const
+{
+ return tr("Transition Tool");
+}
+
+void TransitionTool::activateTool()
+{
+ view()->changeToCustomTool();
+}
+
+void TransitionTool::unblock()
+{
+ m_block = false;
+}
+
+QGraphicsLineItem *TransitionTool::lineItem()
+{
+ return m_lineItem.get();
+}
+
+QGraphicsRectItem *TransitionTool::rectangleItem1()
+{
+ return m_rectangleItem1.get();
+}
+
+QGraphicsRectItem *TransitionTool::rectangleItem2()
+{
+ return m_rectangleItem2.get();
+}
+
+FormEditorItem *TransitionTool::currentFormEditorItem() const
+{
+ if (scene()->items().contains(m_formEditorItem))
+ return m_formEditorItem;
+
+ return nullptr;
+}
+
+void TransitionTool::createItems() {
+ m_block = true;
+ QTimer::singleShot(200, this, [this](){ unblock(); });
+
+ if (!lineItem())
+ m_lineItem.reset(new QGraphicsLineItem(scene()->manipulatorLayerItem()));
+
+ if (!rectangleItem1())
+ m_rectangleItem1.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
+
+ if (!rectangleItem2())
+ m_rectangleItem2.reset(new QGraphicsRectItem(scene()->manipulatorLayerItem()));
+
+ m_rectangleItem2->setVisible(false);
+
+ QPen pen;
+ pen.setColor(QColor(Qt::lightGray));
+ pen.setStyle(Qt::DashLine);
+ pen.setWidth(0);
+ m_lineItem->setPen(pen);
+
+ pen.setColor(QColor(108, 141, 221));
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidth(4);
+ pen.setCosmetic(true);
+ m_rectangleItem1->setPen(pen);
+
+ m_rectangleItem2->setPen(pen);
+}
+
+void TransitionTool::createTransition(FormEditorItem *source, FormEditorItem *target)
+{
+ QmlFlowTargetNode sourceNode(source->qmlItemNode().modelNode());
+ QmlFlowTargetNode targetNode(target->qmlItemNode().modelNode());
+
+ if (sourceNode.isValid() && targetNode.isValid()
+ && sourceNode != targetNode
+ && !targetNode.isFlowActionArea()
+ && !targetNode.isFlowWildcard()) {
+ view()->executeInTransaction("create transition", [&sourceNode, targetNode](){
+ sourceNode.assignTargetItem(targetNode);
+ });
+ } else {
+ qWarning() << Q_FUNC_INFO << "nodes invalid";
+ }
+}
+
+}
diff --git a/src/plugins/qmldesigner/components/formeditor/transitiontool.h b/src/plugins/qmldesigner/components/formeditor/transitiontool.h
new file mode 100644
index 0000000000..6efa1d177a
--- /dev/null
+++ b/src/plugins/qmldesigner/components/formeditor/transitiontool.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#pragma once
+
+#include "abstractcustomtool.h"
+#include "selectionindicator.h"
+
+#include <QGraphicsLineItem>
+#include <QHash>
+#include <QPointer>
+
+#include <memory>
+
+namespace QmlDesigner {
+
+class TransitionTool : public QObject, public AbstractCustomTool
+{
+ Q_OBJECT
+public:
+ TransitionTool();
+ ~TransitionTool();
+
+ void mousePressEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneMouseEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *keyEvent) override;
+
+ void dragLeaveEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneDragDropEvent * event) override;
+ void dragMoveEvent(const QList<QGraphicsItem*> &itemList,
+ QGraphicsSceneDragDropEvent * event) override;
+
+ void itemsAboutToRemoved(const QList<FormEditorItem*> &itemList) override;
+
+ void selectedItemsChanged(const QList<FormEditorItem*> &itemList) override;
+
+ void instancesCompleted(const QList<FormEditorItem*> &itemList) override;
+ void instancesParentChanged(const QList<FormEditorItem *> &itemList) override;
+ void instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
+
+ void clear() override;
+
+ void formEditorItemsChanged(const QList<FormEditorItem*> &itemList) override;
+
+ int wantHandleItem(const ModelNode &modelNode) const override;
+
+ QString name() const override;
+
+ void activateTool();
+ void unblock();
+
+ QGraphicsLineItem *lineItem();
+ QGraphicsRectItem *rectangleItem1();
+ QGraphicsRectItem *rectangleItem2();
+
+private:
+ FormEditorItem *currentFormEditorItem() const;
+ void createItems();
+ void createTransition(FormEditorItem *item1, FormEditorItem *item2);
+
+ FormEditorItem* m_formEditorItem;
+ std::unique_ptr<QGraphicsLineItem> m_lineItem;
+ std::unique_ptr<QGraphicsRectItem> m_rectangleItem1;
+ std::unique_ptr<QGraphicsRectItem> m_rectangleItem2;
+ bool m_block = true;
+};
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index a65ff3600b..5fd0b7990d 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -38,6 +38,7 @@
#include <sourcetool/sourcetool.h>
#include <colortool/colortool.h>
#include <annotationeditor/annotationtool.h>
+#include <formeditor/transitiontool.h>
#include <texttool/texttool.h>
#include <timelineeditor/timelineview.h>
#include <pathtool/pathtool.h>
@@ -242,6 +243,7 @@ bool QmlDesignerPlugin::delayedInitialize()
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TextTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::PathTool);
+ d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TransitionTool);
return true;
}
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs
index 3c434788cd..c69f5e5c22 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -530,6 +530,8 @@ Project {
"formeditor/toolbox.h",
"formeditor/formeditortoolbutton.cpp",
"formeditor/formeditortoolbutton.h",
+ "formeditor/transitiontool.cpp",
+ "formeditor/transitiontool.h",
"importmanager/importlabel.cpp",
"importmanager/importlabel.h",
"importmanager/importmanagercombobox.cpp",