From f652167768f105ad349833f18ee85a6dd81ae559 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 10 Jun 2020 20:47:45 +0200 Subject: [PATCH 01/46] qmldesigner: add language to create scene command and also save the last used language to settings Task-number: QDS-2218 Change-Id: Ib82f7bc755755661183452b32829be3d048d9947 Reviewed-by: Marco Bubke --- .../qmlpuppet/commands/createscenecommand.cpp | 16 +++++++++-- .../qmlpuppet/commands/createscenecommand.h | 24 +++++++++------- .../instances/nodeinstanceserver.cpp | 26 ++++++++++------- .../qml2puppet/instances/nodeinstanceserver.h | 1 + .../qt5previewnodeinstanceserver.cpp | 1 + .../instances/nodeinstanceview.cpp | 28 +++++++++++-------- src/plugins/qmldesigner/designersettings.cpp | 1 + src/plugins/qmldesigner/designersettings.h | 1 + 8 files changed, 64 insertions(+), 34 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.cpp b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.cpp index 0c69dc0aa4b..1ee34ff4477 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.cpp +++ b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.cpp @@ -40,7 +40,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector &instanc const QVector &importVector, const QVector &mockupTypeVector, const QUrl &fileUrl, - const QHash &edit3dToolStates) + const QHash &edit3dToolStates, + const QString &language) : m_instanceVector(instanceContainer), m_reparentInstanceVector(reparentContainer), m_idVector(idVector), @@ -50,7 +51,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector &instanc m_importVector(importVector), m_mockupTypeVector(mockupTypeVector), m_fileUrl(fileUrl), - m_edit3dToolStates(edit3dToolStates) + m_edit3dToolStates(edit3dToolStates), + m_language(language) { } @@ -104,6 +106,11 @@ QHash CreateSceneCommand::edit3dToolStates() const return m_edit3dToolStates; } +QString CreateSceneCommand::language() const +{ + return m_language; +} + QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) { out << command.instances(); @@ -116,6 +123,7 @@ QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) out << command.mockupTypes(); out << command.fileUrl(); out << command.edit3dToolStates(); + out << command.language(); return out; } @@ -132,6 +140,7 @@ QDataStream &operator>>(QDataStream &in, CreateSceneCommand &command) in >> command.m_mockupTypeVector; in >> command.m_fileUrl; in >> command.m_edit3dToolStates; + in >> command.m_language; return in; } @@ -148,7 +157,8 @@ QDebug operator <<(QDebug debug, const CreateSceneCommand &command) << "imports: " << command.imports() << ", " << "mockupTypes: " << command.mockupTypes() << ", " << "fileUrl: " << command.fileUrl() << ", " - << "edit3dToolStates: " << command.edit3dToolStates() << ")"; + << "edit3dToolStates: " << command.edit3dToolStates() << ", " + << "language: " << command.language() << ")"; } } diff --git a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h index fbfd2d2d5f9..aee8fe0d478 100644 --- a/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h +++ b/share/qtcreator/qml/qmlpuppet/commands/createscenecommand.h @@ -45,16 +45,18 @@ class CreateSceneCommand public: CreateSceneCommand(); - explicit CreateSceneCommand(const QVector &instanceContainer, - const QVector &reparentContainer, - const QVector &idVector, - const QVector &valueChangeVector, - const QVector &bindingChangeVector, - const QVector &auxiliaryChangeVector, - const QVector &importVector, - const QVector &mockupTypeVector, - const QUrl &fileUrl, - const QHash &edit3dToolStates); + explicit CreateSceneCommand( + const QVector &instanceContainer, + const QVector &reparentContainer, + const QVector &idVector, + const QVector &valueChangeVector, + const QVector &bindingChangeVector, + const QVector &auxiliaryChangeVector, + const QVector &importVector, + const QVector &mockupTypeVector, + const QUrl &fileUrl, + const QHash &edit3dToolStates, + const QString &language); QVector instances() const; QVector reparentInstances() const; @@ -66,6 +68,7 @@ public: QVector mockupTypes() const; QUrl fileUrl() const; QHash edit3dToolStates() const; + QString language() const; private: QVector m_instanceVector; @@ -78,6 +81,7 @@ private: QVector m_mockupTypeVector; QUrl m_fileUrl; QHash m_edit3dToolStates; + QString m_language; }; QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 561076d080b..9b0b59d58bb 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -314,6 +314,7 @@ void NodeInstanceServer::stopRenderTimer() void NodeInstanceServer::createScene(const CreateSceneCommand &command) { + setTranslationLanguage(command.language()); initializeView(); Internal::QmlPrivateGate::stopUnifiedTimer(); @@ -1329,6 +1330,20 @@ void NodeInstanceServer::loadDummyContextObjectFile(const QFileInfo& qmlFileInfo refreshBindings(); } +void NodeInstanceServer::setTranslationLanguage(const QString &language) +{ + static QPointer multilanguageTranslator; + if (!MultiLanguage::databaseFilePath().isEmpty()) { + if (!multilanguageLink) { + multilanguageLink = std::make_unique(); + multilanguageTranslator = multilanguageLink->translator().release(); + QCoreApplication::installTranslator(multilanguageTranslator); + } + if (multilanguageTranslator) + multilanguageTranslator->setLanguage(language); + } +} + void NodeInstanceServer::loadDummyDataFiles(const QString& directory) { QDir dir(directory, "*.qml"); @@ -1400,16 +1415,7 @@ void NodeInstanceServer::view3DAction(const View3DActionCommand &command) void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command) { - static QPointer multilanguageTranslator; - if (!MultiLanguage::databaseFilePath().isEmpty()) { - if (!multilanguageLink) { - multilanguageLink = std::make_unique(); - multilanguageTranslator = multilanguageLink->translator().release(); - QCoreApplication::installTranslator(multilanguageTranslator); - } - if (multilanguageTranslator) - multilanguageTranslator->setLanguage(command.language); - } + setTranslationLanguage(command.language); QEvent ev(QEvent::LanguageChange); QCoreApplication::sendEvent(QCoreApplication::instance(), &ev); engine()->retranslate(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index 973edd4f1f0..87eb5a1b7e9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -236,6 +236,7 @@ protected: virtual void initializeView() = 0; virtual void setupScene(const CreateSceneCommand &command) = 0; + void setTranslationLanguage(const QString &language); void loadDummyDataFiles(const QString& directory); void loadDummyDataContext(const QString& directory); void loadDummyDataFile(const QFileInfo& fileInfo); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index a54dfbb82b6..6e500120634 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -46,6 +46,7 @@ Qt5PreviewNodeInstanceServer::Qt5PreviewNodeInstanceServer(NodeInstanceClientInt void Qt5PreviewNodeInstanceServer::createScene(const CreateSceneCommand &command) { + setTranslationLanguage(command.language()); initializeView(); setupScene(command); startRenderTimer(); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 678bb1ea943..96dd127f28d 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -69,6 +69,7 @@ #include "variantproperty.h" #include "view3dactioncommand.h" +#include #include #include #include @@ -540,7 +541,9 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, } } } else if (node.isRootNode() && name == "language@Internal") { - nodeInstanceServer()->changeLanguage({value.toString()}); + const QString languageAsString = value.toString(); + DesignerSettings::setValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE, languageAsString); + nodeInstanceServer()->changeLanguage({languageAsString}); } else if (node.isRootNode() && name == "previewSize@Internal") { nodeInstanceServer()->changePreviewImageSize(value.toSize()); } @@ -982,16 +985,19 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() } - return CreateSceneCommand(instanceContainerList, - reparentContainerList, - idContainerList, - valueContainerList, - bindingContainerList, - auxiliaryContainerVector, - importVector, - mockupTypesVector, - model()->fileUrl(), - m_edit3DToolStates[model()->fileUrl()]); + return CreateSceneCommand( + instanceContainerList, + reparentContainerList, + idContainerList, + valueContainerList, + bindingContainerList, + auxiliaryContainerVector, + importVector, + mockupTypesVector, + model()->fileUrl(), + m_edit3DToolStates[model()->fileUrl()], + DesignerSettings::getValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE).toString() + ); } ClearSceneCommand NodeInstanceView::createClearSceneCommand() const diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index 91091c53d66..96504f3a1cc 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -85,6 +85,7 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::SIMPLE_COLOR_PALETTE_CONTENT, QStringList()); restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); restoreValue(settings, DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER, true); + restoreValue(settings, DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE, "en"); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index 4d25edb8bc0..a18bd206877 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -68,6 +68,7 @@ const char ENABLE_TIMELINEVIEW[] = "EnableTimelineView"; const char SIMPLE_COLOR_PALETTE_CONTENT[] = "SimpleColorPaletteContent"; const char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode"; const char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer"; +const char LAST_USED_TRANSLATION_LANGUAGE[] = "LastUsedTranslationLanguage"; } class DesignerSettings : public QHash From 72bfea7b0176c5ce3e0afa856e54efee033c637d Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 11 Jun 2020 16:38:51 +0200 Subject: [PATCH 02/46] qmlpreview: set language before it starts Task-number: QDS-2218 Change-Id: I1d9e73b1bbaf2280070a9484cefe679c048c357b Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/designersettings.h | 5 +++-- .../qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index a18bd206877..746465386d7 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -25,7 +25,8 @@ #pragma once -#include +#include + #include #include #include @@ -71,7 +72,7 @@ const char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer" const char LAST_USED_TRANSLATION_LANGUAGE[] = "LastUsedTranslationLanguage"; } -class DesignerSettings : public QHash +class QMLDESIGNERCORE_EXPORT DesignerSettings : public QHash { public: DesignerSettings(); diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp index 1fe99825c37..32d524f6909 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp @@ -27,6 +27,7 @@ #include "qmlpreviewactions.h" #include +#include #include #include @@ -48,8 +49,8 @@ static void handleAction(const SelectionContext &context) { if (context.view()->isAttached()) { if (context.toggled()) { + QmlPreviewPlugin::setLanguageLocale(DesignerSettings::getValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE).toString()); ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE); - QmlPreviewPlugin::setQmlFile(); } else { QmlPreviewPlugin::stopAllRunControls(); } From 2f66b1d49a71fbabd81de79c4d51e4804ea0fd8d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Jun 2020 19:10:19 +0200 Subject: [PATCH 03/46] QmlDesigner: Rename 3d-active-scene to active3dScene For annotations this has to be a valid QML property. Task-number: QDS-2269 Change-Id: Ib6483a9dd673ddf24b8688909c599a518dfb361c Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 2 +- .../qmldesigner/components/integration/designdocument.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 9e882be2fde..fda18bdf45f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -112,7 +112,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); edit3DWidget()->canvas()->updateActiveScene(newActiveScene); - rootModelNode().setAuxiliaryData("3d-active-scene", newActiveScene); + rootModelNode().setAuxiliaryData("active3dScene", newActiveScene); } if (sceneState.contains(selectKey)) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index e5d7e8978dc..6dc23eabf55 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -469,7 +469,7 @@ void DesignDocument::paste() [](const ModelNode &node) { return !node.isSubclassOf("QtQuick3D.Node"); }) == selectedNodes.end(); if (all3DNodes) { - int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt(); + int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt(); if (activeSceneId != -1) { NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId); @@ -515,7 +515,7 @@ void DesignDocument::paste() } else { // if selection is empty and this is a 3D Node, paste it under the active scene if (pastedNode.isSubclassOf("QtQuick3D.Node")) { - int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt(); + int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt(); if (activeSceneId != -1) { NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId); From 04be31a7f3a15db9d5fb7a484b16e13ad55bec18 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Jun 2020 19:11:04 +0200 Subject: [PATCH 04/46] QmlDesigner: Do not resize FlowItems Change-Id: I19d49c05b4119caa38cb554eaea57dc888835f4e Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 6597a62dde5..de500197c18 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -279,7 +279,8 @@ bool QmlItemNode::modelIsResizable() const return !modelNode().hasBindingProperty("width") && !modelNode().hasBindingProperty("height") && itemIsResizable(modelNode()) - && !modelIsInLayout(); + && !modelIsInLayout() + && !isFlowItem(); } bool QmlItemNode::modelIsInLayout() const From ce2342397ed2288a5787574bb9ad7771e58aec68 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Jun 2020 19:10:39 +0200 Subject: [PATCH 05/46] QmlDesigner: Do not allow invalid annotations Task-number: QDS-2269 Change-Id: Ib98cce353d7a92222013b0c2033a7524dddd5403 Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/rewriterview.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 4d8ebd43152..f0fd181e79b 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -53,6 +53,8 @@ #include #include +#include + #include #include @@ -530,6 +532,9 @@ QString RewriterView::auxiliaryDataAsQML() const QTC_ASSERT(!m_canonicalIntModelNode.isEmpty(), return {}); int columnCount = 0; + + const QRegExp safeName("[a-z][a-zA-Z0-9]*"); + for (const auto &node : allModelNodes()) { QHash data = node.auxiliaryData(); if (!data.isEmpty()) { @@ -562,6 +567,9 @@ QString RewriterView::auxiliaryDataAsQML() const if (idIsQmlKeyWord(key)) continue; + if (!safeName.exactMatch(key)) + continue; + const QVariant value = data.value(key.toUtf8()); QString strValue = value.toString(); From 3ad76aa57f75a41ea5dd1c35e4682c657ac32ce4 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Jun 2020 19:09:19 +0200 Subject: [PATCH 06/46] QmlDesigner: Add trigger "action" for transition Change-Id: Ia498af6ad5014fbae61bd51ddb066ce61ba647b5 Reviewed-by: Thomas Hartmann --- .../components/connectioneditor/connectionmodel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f4fa65e52de..3d38576e276 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -271,6 +271,9 @@ void ConnectionModel::addConnection() if (QmlItemNode(selectedNode).isFlowActionArea()) source = selectedNode.validId() + ".trigger()"; + if (QmlVisualNode(selectedNode).isFlowTransition()) + source = selectedNode.validId() + ".trigger()"; + if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty()) newNode.bindingProperty("target").setExpression(selectedNode.id()); else From f5316561396e4034848ead6539605beefe5edb91 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 11 Jun 2020 16:43:47 +0200 Subject: [PATCH 07/46] QmlDesigner: Fix FormEditorItem bounding rect Change-Id: Ieef8f299e620d5dc091703c6b48847f972d621f6 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/formeditor/formeditoritem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 946a3e72e79..253d5ce529e 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -129,7 +129,7 @@ void FormEditorItem::setup() QRectF FormEditorItem::boundingRect() const { - return m_boundingRect.adjusted(-2, -2, 2, 2); + return m_boundingRect; } QPainterPath FormEditorItem::shape() const @@ -150,7 +150,7 @@ void FormEditorItem::updateGeometry() prepareGeometryChange(); m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect(); - m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect); + m_boundingRect = qmlItemNode().instanceBoundingRect(); setTransform(qmlItemNode().instanceTransformWithContentTransform()); // the property for zValue is called z in QGraphicsObject if (qmlItemNode().instanceValue("z").isValid() && !qmlItemNode().isRootModelNode()) From b24bb3d9a7ec4fa48c10b8ca7a5074e623f7b067 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 11 Jun 2020 16:49:53 +0200 Subject: [PATCH 08/46] QmlDesigner: Fix isValid functionality Change-Id: I20c2b709278cb1f5fbfa9ca4a1ce45b962fceab4 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/formeditor/formeditoritem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 253d5ce529e..227a82530ed 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -740,7 +740,7 @@ static bool isValid(const QList &list) if (!item.isValid()) return false; - return true; + return !list.isEmpty(); } static bool isModelNodeValid(const QList &list) @@ -749,7 +749,7 @@ static bool isModelNodeValid(const QList &list) if (!item.modelNode().isValid()) return false; - return true; + return !list.isEmpty(); } class ResolveConnection From d52cb22b441d1d95e06814506e63db4884831477 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 11 Jun 2020 18:35:46 +0200 Subject: [PATCH 09/46] QmlDesigner: Add fit root to screen button * Add fit root to screen action to designer actions * Add button to form editor taskbar to trigger the action * Add additional zoom levels Task-number: QDS-2234 Change-Id: I1310da8ee9cfa608ed0c28f8bff769d320f588c3 Reviewed-by: Thomas Hartmann --- .../componentcore/componentcore_constants.h | 5 +++++ .../componentcore/designeractionmanager.cpp | 10 +++++++++ .../componentcore/modelnodeoperations.cpp | 8 +++++++ .../componentcore/modelnodeoperations.h | 1 + .../components/componentcore/zoomaction.cpp | 22 +++++++++++++++---- .../components/componentcore/zoomaction.h | 3 +++ .../components/formeditor/formeditorview.cpp | 22 +++++++++++++++++++ 7 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 792666ec364..e6ecc114b73 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -83,6 +83,7 @@ const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedC const char flowAssignEffectCommandId[] = "AssignFlowEffect"; const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect"; const char addToGroupItemCommandId[] = "AddToGroupItem"; +const char fitRootToScreenCommandId[] = "FitRootToScreen"; const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); @@ -155,6 +156,8 @@ const char layoutFillHeightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContext const char flowAssignEffectDisplayName[] = "Assign FlowEffect "; const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect "; +const char fitRootToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit root to screen"); + const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected item."); const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected item."); @@ -173,6 +176,8 @@ const char decreaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesi const char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add item to stacked container."); const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action."); +const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space."); + const int priorityFirst = 280; const int prioritySelectionCategory = 220; const int priorityQmlPreviewCategory = 200; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 2dbf8b00b44..ceb8ddcdd0b 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -821,6 +821,16 @@ void DesignerActionManager::createDefaultDesignerActions() &resetSize, &selectionNotEmptyAndHasWidthOrHeightProperty)); + addDesignerAction(new ModelNodeAction( + fitRootToScreenCommandId, + fitRootToScreenDisplayName, + Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(), + fitRootToScreenToolTip, + genericToolBarCategory, + QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_0), + 182, + &fitRootToScreen)); + addDesignerAction(new SeperatorDesignerAction(editCategory, 170)); addDesignerAction(new VisiblityModelNodeAction( diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index e91fde43599..c73f48d6af0 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -344,6 +344,14 @@ void resetPosition(const SelectionContext &selectionState) }); } +void fitRootToScreen(const SelectionContext &selectionState) +{ + if (!selectionState.view()) + return; + + selectionState.view()->emitCustomNotification(QStringLiteral("fit root to screen")); +} + void goIntoComponentOperation(const SelectionContext &selectionState) { goIntoComponent(selectionState.currentSingleSelectedNode()); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index afd8416bf9f..b8bbc0e1c90 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -49,6 +49,7 @@ void setFillWidth(const SelectionContext &selectionState); void setFillHeight(const SelectionContext &selectionState); void resetSize(const SelectionContext &selectionState); void resetPosition(const SelectionContext &selectionState); +void fitRootToScreen(const SelectionContext &selectionState); void goIntoComponentOperation(const SelectionContext &selectionState); void setId(const SelectionContext &selectionState); void resetZ(const SelectionContext &selectionState); diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp index 95ed102f82b..4f1d1c3da90 100644 --- a/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.cpp @@ -30,7 +30,7 @@ namespace QmlDesigner { -const int defaultZoomIndex = 11; +const int defaultZoomIndex = 13; ZoomAction::ZoomAction(QObject *parent) : QWidgetAction(parent), @@ -69,14 +69,19 @@ void ZoomAction::setZoomLevel(float zoomLevel) if (qFuzzyCompare(m_zoomLevel, zoomLevel)) return; + forceZoomLevel(zoomLevel); +} + +void ZoomAction::forceZoomLevel(float zoomLevel) +{ m_zoomLevel = qBound(0.01f, zoomLevel, 16.0f); emit zoomLevelChanged(m_zoomLevel); } //initial m_zoomLevel and m_currentComboBoxIndex -const QVector s_zoomFactors = {0.01f, 0.02f, 0.05f, 0.0625f, 0.125f, 0.25f, - 0.33f, 0.5f, 0.66f, 0.75f, 0.9f, 1.0f, 1.25f, - 1.5f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f }; +const QVector s_zoomFactors = {0.01f, 0.02f, 0.05f, 0.0625f, 0.1f, 0.125f, 0.2f, 0.25f, + 0.33f, 0.5f, 0.66f, 0.75f, 0.9f, 1.0f, 1.1f, 1.25f, 1.33f, + 1.5f, 1.66f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f }; int getZoomIndex(float zoom) { @@ -87,6 +92,15 @@ int getZoomIndex(float zoom) return -1; } +float ZoomAction::getClosestZoomLevel(float zoomLevel) +{ + int i = 0; + while (i < s_zoomFactors.size() && s_zoomFactors[i] < zoomLevel) + ++i; + + return s_zoomFactors[qBound(0, i - 1, s_zoomFactors.size() - 1)]; +} + QWidget *ZoomAction::createWidget(QWidget *parent) { auto comboBox = new QComboBox(parent); diff --git a/src/plugins/qmldesigner/components/componentcore/zoomaction.h b/src/plugins/qmldesigner/components/componentcore/zoomaction.h index 1b178343e8d..bd46f915d91 100644 --- a/src/plugins/qmldesigner/components/componentcore/zoomaction.h +++ b/src/plugins/qmldesigner/components/componentcore/zoomaction.h @@ -48,6 +48,9 @@ public: void zoomOut(); void resetZoomLevel(); void setZoomLevel(float zoomLevel); + void forceZoomLevel(float zoomLevel); + + static float getClosestZoomLevel(float zoomLevel); protected: QWidget *createWidget(QWidget *parent) override; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index c7c34329c4a..db2d10849f4 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -448,6 +448,28 @@ void FormEditorView::customNotification(const AbstractView * /*view*/, const QSt m_dragTool->clearMoveDelay(); if (identifier == QLatin1String("reset QmlPuppet")) temporaryBlockView(); + if (identifier == QLatin1String("fit root to screen")) { + + if (QmlItemNode(rootModelNode()).isFlowView()) { + QRectF boundingRect; + for (QGraphicsItem *item : scene()->items()) { + if (auto formEditorItem = FormEditorItem::fromQGraphicsItem(item)) { + if (!formEditorItem->qmlItemNode().modelNode().isRootNode() + && !formEditorItem->sceneBoundingRect().isNull()) + boundingRect = boundingRect.united(formEditorItem->sceneBoundingRect()); + } + } + m_formEditorWidget->graphicsView()->fitInView(boundingRect, + Qt::KeepAspectRatio); + } else { + m_formEditorWidget->graphicsView()->fitInView(m_formEditorWidget->rootItemRect(), + Qt::KeepAspectRatio); + } + + const qreal scaleFactor = m_formEditorWidget->graphicsView()->viewportTransform().m11(); + float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor); + m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel); + } } AbstractFormEditorTool *FormEditorView::currentTool() const From fe5636e53b1be9fcc0f8a5d302163eb42cec0b6b Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 12 Jun 2020 14:28:55 +0200 Subject: [PATCH 10/46] QmlDesigner: Add fit selection to screen button * Add fit selection to screen action to designer actions * Add button to form editor taskbar to trigger the action Change-Id: I3774802f034892ea07782717c769c5141eae4bea Reviewed-by: Thomas Hartmann --- .../componentcore/componentcore_constants.h | 3 +++ .../componentcore/designeractionmanager.cpp | 11 +++++++++++ .../componentcore/modelnodeoperations.cpp | 9 +++++++++ .../componentcore/modelnodeoperations.h | 1 + .../components/formeditor/formeditorview.cpp | 19 +++++++++++++++++-- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index e6ecc114b73..2fa19a5c243 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -84,6 +84,7 @@ const char flowAssignEffectCommandId[] = "AssignFlowEffect"; const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect"; const char addToGroupItemCommandId[] = "AddToGroupItem"; const char fitRootToScreenCommandId[] = "FitRootToScreen"; +const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen"; const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); @@ -157,6 +158,7 @@ const char flowAssignEffectDisplayName[] = "Assign FlowEffect "; const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect "; const char fitRootToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit root to screen"); +const char fitSelectionToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit selection to screen"); const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected item."); const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected item."); @@ -177,6 +179,7 @@ const char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerCo const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action."); const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space."); +const char fitSelectionToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the selected elements inside the available space."); const int priorityFirst = 280; const int prioritySelectionCategory = 220; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index ceb8ddcdd0b..479f40203cb 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -831,6 +831,17 @@ void DesignerActionManager::createDefaultDesignerActions() 182, &fitRootToScreen)); + addDesignerAction(new ModelNodeAction( + fitSelectionToScreenCommandId, + fitSelectionToScreenDisplayName, + Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(), + fitSelectionToScreenToolTip, + genericToolBarCategory, + QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_I), + 183, + &fitSelectionToScreen, + &selectionNotEmpty)); + addDesignerAction(new SeperatorDesignerAction(editCategory, 170)); addDesignerAction(new VisiblityModelNodeAction( diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index c73f48d6af0..71d1ec4534a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -352,6 +352,15 @@ void fitRootToScreen(const SelectionContext &selectionState) selectionState.view()->emitCustomNotification(QStringLiteral("fit root to screen")); } +void fitSelectionToScreen(const SelectionContext &selectionState) +{ + if (!selectionState.view()) + return; + + selectionState.view()->emitCustomNotification(QStringLiteral("fit selection to screen"), + selectionState.selectedModelNodes()); +} + void goIntoComponentOperation(const SelectionContext &selectionState) { goIntoComponent(selectionState.currentSingleSelectedNode()); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index b8bbc0e1c90..0a8e094a7e1 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -50,6 +50,7 @@ void setFillHeight(const SelectionContext &selectionState); void resetSize(const SelectionContext &selectionState); void resetPosition(const SelectionContext &selectionState); void fitRootToScreen(const SelectionContext &selectionState); +void fitSelectionToScreen(const SelectionContext &selectionState); void goIntoComponentOperation(const SelectionContext &selectionState); void setId(const SelectionContext &selectionState); void resetZ(const SelectionContext &selectionState); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index db2d10849f4..f64b45f3517 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -442,14 +442,13 @@ void FormEditorView::documentMessagesChanged(const QList &error m_formEditorWidget->hideErrorMessageBox(); } -void FormEditorView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList &/*nodeList*/, const QList &/*data*/) +void FormEditorView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList &nodeList, const QList &/*data*/) { if (identifier == QLatin1String("puppet crashed")) m_dragTool->clearMoveDelay(); if (identifier == QLatin1String("reset QmlPuppet")) temporaryBlockView(); if (identifier == QLatin1String("fit root to screen")) { - if (QmlItemNode(rootModelNode()).isFlowView()) { QRectF boundingRect; for (QGraphicsItem *item : scene()->items()) { @@ -470,6 +469,22 @@ void FormEditorView::customNotification(const AbstractView * /*view*/, const QSt float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor); m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel); } + if (identifier == QLatin1String("fit selection to screen")) { + if (nodeList.isEmpty()) + return; + + QRectF boundingRect; + for (const ModelNode &node : nodeList) { + if (FormEditorItem *item = scene()->itemForQmlItemNode(node)) + boundingRect = boundingRect.united(item->sceneBoundingRect()); + } + + m_formEditorWidget->graphicsView()->fitInView(boundingRect, + Qt::KeepAspectRatio); + const qreal scaleFactor = m_formEditorWidget->graphicsView()->viewportTransform().m11(); + float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor); + m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel); + } } AbstractFormEditorTool *FormEditorView::currentTool() const From 818e263122287d45d8d7c86e74d766b3f4843ba1 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 4 Jun 2020 22:31:32 +0200 Subject: [PATCH 11/46] AssetExporter: Add basic infrastructure for the plugin Adds export action, basic UI and workflow classes Change-Id: If019a8fa48cacaf7e7665335c53b3adeeb257b07 Reviewed-by: Thomas Hartmann --- .../assetexporterplugin/assetexportdialog.cpp | 94 +++++++ .../assetexporterplugin/assetexportdialog.h | 66 +++++ .../assetexporterplugin/assetexportdialog.ui | 34 +++ .../assetexporterplugin/assetexporter.cpp | 255 ++++++++++++++++++ .../assetexporterplugin/assetexporter.h | 105 ++++++++ .../assetexporterplugin.cpp | 124 +++++++++ .../assetexporterplugin/assetexporterplugin.h | 56 ++++ .../assetexporterplugin.metainfo | 2 + .../assetexporterplugin.pri | 29 ++ .../assetexporterplugin.pro | 17 ++ .../assetexporterplugin.qrc | 5 + .../assetexporterplugin/assetexporterview.cpp | 149 ++++++++++ .../assetexporterplugin/assetexporterview.h | 85 ++++++ .../assetexportpluginconstants.h | 33 +++ .../assetexporterplugin/componentexporter.cpp | 107 ++++++++ .../assetexporterplugin/componentexporter.h | 90 +++++++ .../parsers/modelitemnodeparser.cpp | 66 +++++ .../parsers/modelitemnodeparser.h | 43 +++ .../parsers/modelnodeparser.cpp | 35 +++ .../parsers/modelnodeparser.h | 48 ++++ src/plugins/qmldesigner/qmldesigner.pro | 7 +- 21 files changed, 1449 insertions(+), 1 deletion(-) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporter.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/componentexporter.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp new file mode 100644 index 00000000000..f43b5a96b1d --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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 "assetexportdialog.h" +#include "ui_assetexportdialog.h" + +#include "utils/fileutils.h" + +#include +#include +#include + +#include + +namespace QmlDesigner { + +AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, + AssetExporter &assetExporter, QWidget *parent) : + QDialog(parent), + m_assetExporter(assetExporter), + m_ui(new Ui::AssetExportDialog), + m_model(this) +{ + m_ui->setupUi(this); + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + + m_ui->filesView->setModel(&m_model); + + m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole); + m_exportBtn->setEnabled(false); + connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport); + m_ui->exportPathEdit->setText(exportPath.toString()); + + connect(&m_assetExporter, &AssetExporter::qmlFileResult, this, [this] (const Utils::FilePath &path) { + m_qmlFiles.append(path); + QStringList files = m_model.stringList(); + files.append(path.toString()); + m_model.setStringList(files); + }); + + connect(&m_assetExporter, &AssetExporter::stateChanged, + this, &AssetExportDialog::onExportStateChanged); +} + +AssetExportDialog::~AssetExportDialog() +{ + m_assetExporter.cancel(); +} + +void AssetExportDialog::onExport() +{ + m_assetExporter.exportQml(m_qmlFiles, + Utils::FilePath::fromString(m_ui->exportPathEdit->text())); +} + +void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState) +{ + switch (newState) { + case AssetExporter::ParsingState::PreProcessing: + m_model.setStringList({}); + break; + case AssetExporter::ParsingState::ExportingDone: + QMessageBox::information(this, tr("QML Export"), tr("Done")); + break; + default: + break; + } + + m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::PreProcessingFinished || + newState == AssetExporter::ParsingState::ExportingDone); + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy()); +} +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h new file mode 100644 index 00000000000..bad4b081954 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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 "assetexporter.h" + +#include +#include + +#include "utils/fileutils.h" + +#include + +QT_BEGIN_NAMESPACE +class QPushButton; +QT_END_NAMESPACE + +namespace Ui { +class AssetExportDialog; +} + +namespace QmlDesigner { + +class AssetExportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter, + QWidget *parent = nullptr); + ~AssetExportDialog(); + +private: + void onExport(); + void onExportStateChanged(AssetExporter::ParsingState newState); + +private: + AssetExporter &m_assetExporter; + std::unique_ptr m_ui; + QPushButton *m_exportBtn = nullptr; + QStringListModel m_model; + Utils::FilePaths m_qmlFiles; +}; + +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui new file mode 100644 index 00000000000..afe83d257dd --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -0,0 +1,34 @@ + + + AssetExportDialog + + + + 0 + 0 + 768 + 480 + + + + Export QML + + + + + + + + + + + + QDialogButtonBox::Cancel + + + + + + + + diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp new file mode 100644 index 00000000000..b667584529f --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** 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 "assetexporter.h" +#include "componentexporter.h" + +#include "plaintexteditmodifier.h" +#include "rewriterview.h" + +#include "projectexplorer/project.h" +#include "projectexplorer/projectnodes.h" + +#include "utils/runextensions.h" +#include "utils/qtcassert.h" + +#include +#include +#include +#include + +#include + +using namespace ProjectExplorer; + +namespace { +Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg) +Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg) +Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg) + +void findQmlFiles(QFutureInterface &f, const Project *project) +{ + if (!project && !f.isCanceled()) + f.reportFinished({}); + + int index = 0; + Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool { + if (f.isCanceled()) + return false; + Utils::FilePath path = node->filePath(); + bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper(); + if (isComponent && node->filePath().endsWith(".ui.qml")) + f.reportResult(path, index++); + return true; + }); + f.reportFinished(); +} + +//static QmlDesigner::Model* createModel(const Utils::FilePath &fileName) +//{ +// QmlDesigner::Model *model = QmlDesigner::Model::create("Item", 2, 7); + +// Utils::FileReader reader; +// QTC_ASSERT(reader.fetch(fileName.toString()), return nullptr); + +// auto textEdit = new QPlainTextEdit; +// textEdit->setPlainText(QString::fromUtf8(reader.data())); + +// auto modifier = new QmlDesigner::NotIndentingTextEditModifier(textEdit); +// modifier->setParent(model); + +// auto rewriterView = new QmlDesigner::RewriterView(QmlDesigner::RewriterView::Validate, model); +// rewriterView->setCheckSemanticErrors(false); +// rewriterView->setTextModifier(modifier); + +// model->attachView(rewriterView); + +// QTC_ASSERT(rewriterView->rootModelNode().isValid(), return nullptr); +// return model; +//} + +} + +namespace QmlDesigner { + +AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) : + QObject(parent), + m_currentState(*this), + m_project(project), + m_view(view) +{ + connect(m_view, &AssetExporterView::loadingFinished, this, &AssetExporter::onQmlFileLoaded); + connect(m_view, &AssetExporterView::loadingError, this, &AssetExporter::notifyLoadError); +} + +AssetExporter::~AssetExporter() +{ + cancel(); +} + +bool AssetExporter::preProcessProject() +{ + if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && + !m_preprocessWatcher->isFinished()) { + qCDebug(loggerInfo) << "Previous pre-processing not finished."; + return false; + } + + m_currentState.change(ParsingState::PreProcessing); + m_preprocessWatcher.reset(new QFutureWatcher(this)); + connect(m_preprocessWatcher.get(), &QFutureWatcher::resultReadyAt, this, + [this](int index) { + emit qmlFileResult(m_preprocessWatcher->resultAt(index)); + }); + + connect(m_preprocessWatcher.get(), &QFutureWatcher::finished, this, + [this] () { m_currentState.change(ParsingState::PreProcessingFinished); }); + + QFuture f = Utils::runAsync(&findQmlFiles, m_project); + m_preprocessWatcher->setFuture(f); + return true; +} + +void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, + bool exportAssets) +{ + // TODO Asset export + Q_UNUSED(exportAssets); + m_exportFiles = qmlFiles; + m_components = QJsonArray(); + m_exportPath = exportPath; + m_currentState.change(ParsingState::Parsing); + triggerLoadNextFile(); +} + +void AssetExporter::cancel() +{ + if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && + !m_preprocessWatcher->isFinished()) { + m_preprocessWatcher->cancel(); + m_preprocessWatcher->waitForFinished(); + } +} + +bool AssetExporter::isBusy() const +{ + return m_currentState == AssetExporter::ParsingState::PreProcessing || + m_currentState == AssetExporter::ParsingState::Parsing || + m_currentState == AssetExporter::ParsingState::ExportingAssets || + m_currentState == AssetExporter::ParsingState::WritingJson; +} + +void AssetExporter::exportComponent(const ModelNode &rootNode) +{ + qCDebug(loggerInfo) << "Exporting component" << rootNode.id(); + ComponentExporter exporter(rootNode); + QJsonObject json = exporter.exportComponent(); + m_components.append(json); +} + +void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) +{ + QString errorStr = tr("Unknown error."); + switch (state) { + case AssetExporterView::LoadState::Exausted: + errorStr = tr("Loading file is taking too long."); + break; + case AssetExporterView::LoadState::QmlErrorState: + errorStr = tr("Cannot parse. QML file has errors."); + break; + default: + return; + } + // TODO. Communicate errors to user + qCDebug(loggerError) << "QML load error:" << errorStr; +} + +void AssetExporter::onQmlFileLoaded() +{ + QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return); + qCDebug(loggerInfo) << "Qml file load done" << m_view->model()->fileUrl(); + exportComponent(m_view->rootModelNode()); + triggerLoadNextFile(); +} + +void AssetExporter::triggerLoadNextFile() +{ + QTimer::singleShot(0, this, &AssetExporter::loadNextFile); +} + +void AssetExporter::loadNextFile() +{ + if (m_exportFiles.isEmpty()) { + m_currentState.change(ParsingState::ParsingFinished); + writeMetadata(); + return; + } + + // Load the next pending file. + const Utils::FilePath file = m_exportFiles.takeFirst(); + qCDebug(loggerInfo) << "Loading next file" << file; + m_view->loadQmlFile(file); +} + +void AssetExporter::writeMetadata() const +{ + qCDebug(loggerInfo) << "Writing metadata"; + m_currentState.change(ParsingState::WritingJson); + QJsonObject jsonRoot; // TODO: Write plugin info to root + jsonRoot.insert("artboards", m_components); + QJsonDocument doc(jsonRoot); + if (doc.isNull() || doc.isEmpty()) { + qCDebug(loggerWarn) << "Empty JSON document"; + } else { + Utils::FileSaver saver(m_exportPath.toString(), QIODevice::Text); + saver.write(doc.toJson(QJsonDocument::Indented)); + if (!saver.finalize()) { + qCDebug(loggerError) << "Cannot write Metadata file: " << saver.errorString(); + } + } + m_currentState.change(ParsingState::ExportingDone); +} + +AssetExporter::State::State(AssetExporter &exporter) : + m_assetExporter(exporter) +{ + +} + +void AssetExporter::State::change(const ParsingState &state) +{ + qCDebug(loggerInfo()) << "Assetimporter State change: Old: " << m_state << "New: " << state; + if (m_state != state) { + m_state = state; + m_assetExporter.stateChanged(m_state); + } +} + +QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s) +{ + os << static_cast::type>(s); + return os; +} + +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h new file mode 100644 index 00000000000..3f13e85123e --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 "assetexporterview.h" +#include "utils/fileutils.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QJsonArray; +QT_END_NAMESPACE + +namespace ProjectExplorer { +class Project; +} + +namespace QmlDesigner { + +class AssetExporter : public QObject +{ + Q_OBJECT + +public: + + enum class ParsingState { + Idle = 0, + PreProcessing, + PreProcessingFinished, + Parsing, + ParsingFinished, + ExportingAssets, + ExportingAssetsFinished, + WritingJson, + ExportingDone + }; + + AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, + QObject *parent = nullptr); + ~AssetExporter(); + + bool preProcessProject(); + void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets = false); + + void cancel(); + bool isBusy() const; + +signals: + void qmlFileResult(Utils::FilePath); + void stateChanged(ParsingState); + +private: + ParsingState currentState() const { return m_currentState.m_state; } + void exportComponent(const ModelNode &rootNode); + void writeMetadata() const; + void notifyLoadError(AssetExporterView::LoadState state); + void triggerLoadNextFile(); + void loadNextFile(); + + void onQmlFileLoaded(); +private: + mutable class State { + public: + State(AssetExporter&); + void change(const ParsingState &state); + operator ParsingState() const { return m_state; } + AssetExporter &m_assetExporter; + ParsingState m_state = ParsingState::Idle; + } m_currentState; + ProjectExplorer::Project *m_project = nullptr; + AssetExporterView *m_view = nullptr; + Utils::FilePaths m_exportFiles; + Utils::FilePath m_exportPath; + QJsonArray m_components; + std::unique_ptr> m_preprocessWatcher; +}; +QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp new file mode 100644 index 00000000000..e23b5168289 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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 "assetexporterplugin.h" + +#include "assetexportpluginconstants.h" +#include "assetexportdialog.h" +#include "assetexporter.h" +#include "assetexporterview.h" +#include "componentexporter.h" + +#include "parsers/modelitemnodeparser.h" + +#include "coreplugin/actionmanager/actionmanager.h" +#include "coreplugin/actionmanager/actioncontainer.h" +#include "coreplugin/documentmanager.h" +#include "qmldesigner/qmldesignerplugin.h" +#include "projectexplorer/projectexplorerconstants.h" +#include "projectexplorer/session.h" +#include "projectexplorer/project.h" +#include "projectexplorer/session.h" + +#include "extensionsystem/pluginmanager.h" +#include "extensionsystem/pluginspec.h" + +#include "utils/algorithm.h" + +#include +#include + +#include + +namespace QmlDesigner { + +AssetExporterPlugin::AssetExporterPlugin() : + m_view(new AssetExporterView) +{ + auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); + auto &viewManager = designerPlugin->viewManager(); + viewManager.registerViewTakingOwnership(m_view); + + // Add parsers templates for factory instantiation. + ComponentExporter::addNodeParser(); + + // Instantiate actions created by the plugin. + addActions(); + + connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, + this, &AssetExporterPlugin::updateActions); + + updateActions(); +} + +QString AssetExporterPlugin::pluginName() const +{ + return QLatin1String("AssetExporterPlugin"); +} + +void AssetExporterPlugin::onExport() +{ + auto startupProject = ProjectExplorer::SessionManager::startupProject(); + if (!startupProject) + return; + + auto exportDir = startupProject->projectFilePath().parentDir(); + if (!exportDir.toFileInfo().isRoot()) + exportDir = exportDir.parentDir(); + auto defaultMetadataPath = exportDir.pathAppended(startupProject->displayName() + ".metadata"); + + AssetExporter assetExporter(m_view, startupProject); + AssetExportDialog assetExporterDialog(defaultMetadataPath, assetExporter); + assetExporter.preProcessProject(); + assetExporterDialog.exec(); +} + +void AssetExporterPlugin::addActions() +{ + auto exportAction = new QAction(tr("Export QML")); + exportAction->setToolTip(tr("Export QML code of the current project.")); + connect(exportAction, &QAction::triggered, this, &AssetExporterPlugin::onExport); + Core::Command *cmd = Core::ActionManager::registerAction(exportAction, Constants::EXPORT_QML); + + // Add action to build menu + Core::ActionContainer *buildMenu = + Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); + buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN); +} + +void AssetExporterPlugin::updateActions() +{ + auto project = ProjectExplorer::SessionManager::startupProject(); + QAction* const exportAction = Core::ActionManager::command(Constants::EXPORT_QML)->action(); + exportAction->setEnabled(project && !project->needsConfiguration()); +} + +QString AssetExporterPlugin::metaInfo() const +{ + return QLatin1String(":/assetexporterplugin/assetexporterplugin.metainfo"); +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h new file mode 100644 index 00000000000..0615cdb2179 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include + + +namespace QmlDesigner { +class AssetExporter; +class AssetExporterView; +class AssetExporterPlugin : public QObject, QmlDesigner::IWidgetPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QmlDesignerPlugin" FILE "assetexporterplugin.json") + + Q_DISABLE_COPY(AssetExporterPlugin) + Q_INTERFACES(QmlDesigner::IWidgetPlugin) + +public: + AssetExporterPlugin(); + + QString metaInfo() const; + QString pluginName() const; + +private: + void onExport(); + void addActions(); + void updateActions(); + + AssetExporterView *m_view = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo new file mode 100644 index 00000000000..5bfe70cffdf --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo @@ -0,0 +1,2 @@ +MetaInfo { +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri new file mode 100644 index 00000000000..17027e9776e --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -0,0 +1,29 @@ +QT *= qml quick core widgets + +VPATH += $$PWD + +RESOURCES += assetexporterplugin.qrc + +INCLUDEPATH += ./ + +HEADERS += \ + assetexportdialog.h \ + assetexporter.h \ + assetexporterplugin.h \ + assetexporterview.h \ + assetexportpluginconstants.h \ + componentexporter.h \ + parsers/modelitemnodeparser.h \ + parsers/modelnodeparser.h + +SOURCES += \ + assetexportdialog.cpp \ + assetexporter.cpp \ + assetexporterplugin.cpp \ + assetexporterview.cpp \ + componentexporter.cpp \ + parsers/modelitemnodeparser.cpp \ + parsers/modelnodeparser.cpp + +FORMS += \ + assetexportdialog.ui diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro new file mode 100644 index 00000000000..2612b06e0e6 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro @@ -0,0 +1,17 @@ +include (../../../../qtcreator.pri) +include (../plugindestdir.pri) +include (../designercore/iwidgetplugin.pri) +include (../qmldesigner_dependencies.pri) +include (assetexporterplugin.pri) + +LIBS += -L$$IDE_PLUGIN_PATH +LIBS += -l$$qtLibraryName(QmlDesigner) +LIBS += -l$$qtLibraryName(ExtensionSystem) +LIBS += -l$$qtLibraryName(Core) +LIBS += -l$$qtLibraryName(ProjectExplorer) +LIBS += -l$$qtLibraryName(Utils) + +TARGET = assetexporterplugin +TEMPLATE = lib +CONFIG += plugin + diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc new file mode 100644 index 00000000000..8db1e0adafb --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc @@ -0,0 +1,5 @@ + + + assetexporterplugin.metainfo + + diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp new file mode 100644 index 00000000000..193ebce62aa --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** 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 "assetexporterview.h" + +#include "qmlitemnode.h" +#include "rewriterview.h" + +#include "coreplugin/editormanager/editormanager.h" +#include "coreplugin/editormanager/ieditor.h" +#include "coreplugin/modemanager.h" +#include "coreplugin/coreconstants.h" + +#include + +namespace { +Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.view", QtInfoMsg) +Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.view", QtWarningMsg) +Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.view", QtCriticalMsg) + +static const int RetryIntervalMs = 500; +static const int MinRetry = 2; +} + +namespace QmlDesigner { + +AssetExporterView::AssetExporterView(QObject *parent) : AbstractView(parent), + m_timer(this) +{ + m_timer.setInterval(RetryIntervalMs); + // We periodically check if file is loaded. + connect(&m_timer, &QTimer::timeout, this, &AssetExporterView::handleTimerTimeout); +} + + +bool AssetExporterView::loadQmlFile(const Utils::FilePath &path, uint timeoutSecs) +{ + qCDebug(loggerInfo) << "Load file" << path; + if (loadingState() == LoadState::Busy) + return false; + + setState(LoadState::Busy); + m_retryCount = std::max(MinRetry, static_cast((timeoutSecs * 1000) / RetryIntervalMs)); + Core::EditorManager::openEditor(path.toString(), Core::Id(), + Core::EditorManager::DoNotMakeVisible); + Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN); + Core::ModeManager::setFocusToCurrentMode(); + m_timer.start(); + return true; +} + +void AssetExporterView::modelAttached(Model *model) +{ + if (model->rewriterView() && model->rewriterView()->inErrorState()) + setState(LoadState::QmlErrorState); + + AbstractView::modelAttached(model); +} + +void AssetExporterView:: +instanceInformationsChanged(const QMultiHash &informationChangeHash) +{ + if (inErrorState() || loadingState() == LoadState::Loaded) + return; // Already reached error or connected state. + + // We expect correct dimensions are available if the rootnode's + // information change message is received. + const auto nodes = informationChangeHash.keys(); + bool hasRootNode = std::any_of(nodes.begin(), nodes.end(), [](const ModelNode &n) { + return n.isRootNode(); + }); + if (hasRootNode) + handleMaybeDone(); +} + +void AssetExporterView::instancesPreviewImageChanged(const QVector &nodeList) +{ + Q_UNUSED(nodeList); + emit previewChanged(); +} + +bool AssetExporterView::inErrorState() const +{ + return m_state == LoadState::Exausted || m_state == LoadState::QmlErrorState; +} + +bool AssetExporterView::isLoaded() const +{ + return isAttached() && QmlItemNode(rootModelNode()).isValid(); +} + +void AssetExporterView::setState(AssetExporterView::LoadState state) +{ + if (state != m_state) { + m_state = state; + qCDebug(loggerInfo) << "Loading state changed" << m_state; + if (inErrorState() || m_state == LoadState::Loaded) { + m_timer.stop(); + if (m_state == LoadState::Loaded) + emit loadingFinished(); + else + emit loadingError(m_state); + } + } +} + +void AssetExporterView::handleMaybeDone() +{ + if (isLoaded()) + setState(LoadState::Loaded); +} + +void AssetExporterView::handleTimerTimeout() +{ + if (!inErrorState() && loadingState() != LoadState::Loaded) + handleMaybeDone(); + + if (--m_retryCount < 0) + setState(LoadState::Exausted); +} + +} + +QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s) +{ + os << static_cast::type>(s); + return os; +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h new file mode 100644 index 00000000000..2fb45d7865c --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 "abstractview.h" + +#include "utils/fileutils.h" + +#include +#include + +#include + +namespace Core { +class IEditor; +} +namespace QmlDesigner { + + +class AssetExporterView : public AbstractView +{ + Q_OBJECT +public: + enum class LoadState { + Idle = 1, + Busy, + Exausted, + QmlErrorState, + Loaded + }; + + AssetExporterView(QObject *parent = nullptr); + + bool loadQmlFile(const Utils::FilePath &path, uint timeoutSecs = 10); + + void modelAttached(Model *model) override; + void instanceInformationsChanged(const QMultiHash &informationChangeHash) override; + void instancesPreviewImageChanged(const QVector &nodeList) override; + + LoadState loadingState() const { return m_state; } + bool inErrorState() const; + +signals: + void loadingFinished(); + void loadingError(LoadState); + void previewChanged(); + +private: + bool isLoaded() const; + void setState(LoadState state); + void handleMaybeDone(); + void handleTimerTimeout(); + + Core::IEditor *m_currentEditor = nullptr; + QTimer m_timer; + int m_retryCount = 0; + LoadState m_state = LoadState::Idle; + bool m_waitForPuppet = false; +}; + +} + +QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h new file mode 100644 index 00000000000..8432e6b8403 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** 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 + +namespace QmlDesigner { +namespace Constants { + +const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml"; + +} +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp new file mode 100644 index 00000000000..a02af72f2ea --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 "componentexporter.h" +#include "parsers/modelnodeparser.h" + +#include "model.h" +#include "nodeabstractproperty.h" +#include "rewriterview.h" + +#include "utils/qtcassert.h" + +#include +#include +#include + +namespace { +Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg) + +static void populateLineage(const QmlDesigner::ModelNode &node, QByteArrayList &lineage) +{ + if (!node.isValid() || node.type().isEmpty()) + return; + lineage.append(node.type()); + if (node.hasParentProperty()) + populateLineage(node.parentProperty().parentModelNode(), lineage); +} + +} + +namespace QmlDesigner { + +std::vector> ComponentExporter::m_readers; +ComponentExporter::ComponentExporter(const ModelNode &rootNode): + m_rootNode(rootNode) +{ + +} + +QJsonObject ComponentExporter::exportComponent() const +{ + QTC_ASSERT(m_rootNode.isValid(), return {}); + return nodeToJson(m_rootNode); +} + +ModelNodeParser *ComponentExporter::createNodeParser(const ModelNode &node) const +{ + QByteArrayList lineage; + populateLineage(node, lineage); + std::unique_ptr reader; + for (auto &parserCreator: m_readers) { + std::unique_ptr r(parserCreator->instance(lineage, node)); + if (r->isExportable()) { + if (reader) { + if (reader->priority() < r->priority()) + reader = std::move(r); + } else { + reader = std::move(r); + } + } + } + if (!reader) { + qCDebug(loggerInfo()) << "No parser for node" << node; + } + return reader.release(); +} + +QJsonObject ComponentExporter::nodeToJson(const ModelNode &node) const +{ + QJsonObject jsonObject; + std::unique_ptr parser(createNodeParser(node)); + if (parser) + jsonObject = parser->json(); + + QJsonArray children; + for (const ModelNode &childnode : node.directSubModelNodes()) + children.append(nodeToJson(childnode)); + + if (!children.isEmpty()) + jsonObject.insert("children", children); + + return jsonObject; +} + + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h new file mode 100644 index 00000000000..3565b190a7b --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include +#include +#include + +#include +#include + +#include "utils/qtcassert.h" + +QT_BEGIN_NAMESPACE +class QJsonArray; +QT_END_NAMESPACE + +namespace QmlDesigner { +class Model; +class ModelNode; +class ComponentExporter; +class ModelNodeParser; + +namespace Internal { +class NodeParserCreatorBase +{ +public: + virtual ~NodeParserCreatorBase() {} +protected: + virtual ModelNodeParser *instance(const QByteArrayList &, const ModelNode &) const = 0; + friend class QmlDesigner::ComponentExporter; +}; + +template +class NodeParserCreator : public NodeParserCreatorBase +{ +public: + NodeParserCreator() = default; + ~NodeParserCreator() = default; + +protected: + ModelNodeParser *instance(const QByteArrayList &lineage, const ModelNode &node) const { + return new T(lineage, node); + } +}; +} //Internal + +class ComponentExporter +{ +public: + ComponentExporter(const ModelNode &rootNode); + + QJsonObject exportComponent() const; + + template static void addNodeParser() + { + QTC_ASSERT((std::is_base_of::value), return); + m_readers.push_back(std::make_unique>()); + } +private: + ModelNodeParser* createNodeParser(const ModelNode &node) const; + QJsonObject nodeToJson(const ModelNode &node) const; + +private: + const ModelNode &m_rootNode; + static std::vector> m_readers; +}; +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp new file mode 100644 index 00000000000..ee89c824ac5 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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 "modelitemnodeparser.h" +#include "assetexportpluginconstants.h" + +#include "modelnode.h" +#include "qmlitemnode.h" +#include "variantproperty.h" + + +namespace QmlDesigner { + +ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node) : + ModelNodeParser(lineage, node) +{ + +} + +bool QmlDesigner::ItemNodeParser::isExportable() const +{ + return m_lineage.contains("QtQuick.Item"); +} + +QJsonObject QmlDesigner::ItemNodeParser::json() const +{ + // TODO parse other relevant properties i.e. dimensions etc + QJsonObject jsonObject; + jsonObject.insert("qmlid", m_node.id()); + QmlItemNode itemNode(m_node); + + // Position relative to parent + QPointF pos = itemNode.instancePosition(); + jsonObject.insert("x", pos.x()); + jsonObject.insert("y", pos.y()); + + // size + QSizeF size = itemNode.instanceSize(); + jsonObject.insert("width", size.width()); + jsonObject.insert("height", size.height()); + + return jsonObject; +} +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h new file mode 100644 index 00000000000..332eada4101 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** 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 "modelnodeparser.h" + +namespace QmlDesigner { +class ModelNode; + +class ItemNodeParser : public ModelNodeParser +{ +public: + ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node); + + ~ItemNodeParser() override = default; + + int priority() const override { return 100; } + bool isExportable() const override; + QJsonObject json() const override; +}; +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp new file mode 100644 index 00000000000..29d6f305e60 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** 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 "modelnodeparser.h" + +namespace QmlDesigner { +ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) : + m_node(node), + m_lineage(lineage) +{ + +} + +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h new file mode 100644 index 00000000000..744abfd3fde --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include +#include + +namespace QmlDesigner { +class ModelNode; + +class ModelNodeParser +{ +public: + ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node); + + virtual ~ModelNodeParser() = default; + + virtual int priority() const = 0; + virtual bool isExportable() const = 0; + virtual QJsonObject json() const = 0; + +protected: + const ModelNode &m_node; + const QByteArrayList m_lineage; +}; +} diff --git a/src/plugins/qmldesigner/qmldesigner.pro b/src/plugins/qmldesigner/qmldesigner.pro index 1986677fac3..43cb1e9be5a 100644 --- a/src/plugins/qmldesigner/qmldesigner.pro +++ b/src/plugins/qmldesigner/qmldesigner.pro @@ -1,4 +1,9 @@ TEMPLATE = subdirs CONFIG += ordered -SUBDIRS = qmldesignerplugin.pro qtquickplugin componentsplugin qmlpreviewplugin +SUBDIRS = \ + qmldesignerplugin.pro \ + qtquickplugin \ + componentsplugin \ + qmlpreviewplugin \ + assetexporterplugin From 9be0988052bf9aaa28813a1b72d6ae66315fb2af Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Mon, 15 Jun 2020 10:59:28 +0200 Subject: [PATCH 12/46] AssetExport: Add missing plugin json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib395cb03610eeabb657e11961cffc22d8d6cd714 Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann --- .../assetexporterplugin/assetexporterplugin.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json new file mode 100644 index 00000000000..a925eaca8e1 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json @@ -0,0 +1,6 @@ +{ + "Vendor" : "The Qt Company Ltd", + "Category" : "Qt Quick", + "Description" : "Plugin for exporting assets and QML from QmlDesigner", + "Url" : "http://www.qt.io" +} From 9a0acda2c9aee63f265e8ddd5012263daa3b24c5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 13 Jun 2020 20:40:03 +0200 Subject: [PATCH 13/46] ClangFormat: Fix warning Change-Id: Ifdf4bfb7662017b2ba2e28074808919f7a726a8a Reviewed-by: Christian Kandeler --- src/plugins/clangformat/clangformatplugin.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/clangformat/clangformatplugin.cpp b/src/plugins/clangformat/clangformatplugin.cpp index 561b7e888ec..9b76a0862ff 100644 --- a/src/plugins/clangformat/clangformatplugin.cpp +++ b/src/plugins/clangformat/clangformatplugin.cpp @@ -153,7 +153,12 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS } return true; #else +#ifdef _MSC_VER +#pragma message( \ + "ClangFormat: building dummy plugin due to unmodified Clang, see README.md for more info") +#else #warning ClangFormat: building dummy plugin due to unmodified Clang, see README.md for more info +#endif *errorString = "Disabling ClangFormat plugin as it has not been built against a modified Clang's libFormat." "For more information see the Qt Creator README at " "https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/README.md"; From b0a8c7066226193a56c6c08dd1caf99367fcd39d Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 15 Jun 2020 17:37:58 +0200 Subject: [PATCH 14/46] QmlDesigner: Fix FlowDecision transition Fix the transition for FlowDecision so it will also connect to QmlFlowItemNode directly without the need of a QmlFlowActionAreaNode. Task-number: QDS-2280 Change-Id: I61a5ab234068bcbc0c28ae43c720d7fc22e941b6 Reviewed-by: Thomas Hartmann --- .../components/formeditor/formeditoritem.cpp | 18 +++---- .../designercore/include/qmlitemnode.h | 3 +- .../designercore/model/qmlitemnode.cpp | 52 +++++++++---------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 227a82530ed..3fc654ce861 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -798,17 +798,17 @@ public: if (f.isValid()) { for (const QmlFlowActionAreaNode &area : f.flowActionAreas()) { ModelNode target = area.targetTransition(); - if (target == node.modelNode()) { + if (target == node.modelNode()) areaNode = area; - } else { - const ModelNode decisionNode = area.decisionNodeForTransition(node.modelNode()); - if (decisionNode.isValid()) { - from.clear(); - from.append(decisionNode); - areaNode = ModelNode(); - } - } } + + const ModelNode decisionNode = QmlFlowItemNode::decisionNodeForTransition(node.modelNode()); + if (decisionNode.isValid()) { + from.clear(); + from.append(decisionNode); + areaNode = ModelNode(); + } + if (f.modelNode().hasAuxiliaryData("joinConnection")) joinConnection = f.modelNode().auxiliaryData("joinConnection").toBool(); } else { diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index d779cc15bf3..f37bae869e6 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -157,7 +157,6 @@ public: void assignTargetFlowItem(const QmlFlowTargetNode &flowItem); QmlFlowItemNode flowItemParent() const; void destroyTarget(); - ModelNode decisionNodeForTransition(const ModelNode &transition) const; }; class QMLDESIGNERCORE_EXPORT QmlFlowItemNode : public QmlItemNode @@ -168,6 +167,8 @@ public: static bool isValidQmlFlowItemNode(const ModelNode &modelNode); QList flowActionAreas() const; QmlFlowViewNode flowView() const; + + static ModelNode decisionNodeForTransition(const ModelNode &transition); }; class QMLDESIGNERCORE_EXPORT QmlFlowViewNode : public QmlItemNode diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index de500197c18..89617f2bc4b 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -559,6 +559,32 @@ QmlFlowViewNode QmlFlowItemNode::flowView() const return QmlFlowViewNode({}); } +ModelNode QmlFlowItemNode::decisionNodeForTransition(const ModelNode &transition) +{ + ModelNode target = transition; + + if (target.isValid() && target.hasMetaInfo() && QmlVisualNode::isFlowTransition(target)) { + + ModelNode finalTarget = target.bindingProperty("to").resolveToModelNode(); + + if (finalTarget.isValid() && finalTarget.hasMetaInfo() && QmlVisualNode::isFlowDecision(finalTarget)) { + if (finalTarget.hasBindingProperty("targets") + && finalTarget.bindingProperty("targets").resolveToModelNodeList().contains(transition)) + return finalTarget; + } + QmlFlowViewNode flowView(transition.view()->rootModelNode()); + if (flowView.isValid()) { + for (const ModelNode target : flowView.decicions()) { + if (target.hasBindingProperty("targets") + && target.bindingProperty("targets").resolveToModelNodeList().contains(transition)) + return target; + } + } + } + + return {}; +} + bool QmlFlowActionAreaNode::isValid() const { return isValidQmlFlowActionAreaNode(modelNode()); @@ -615,32 +641,6 @@ void QmlFlowActionAreaNode::destroyTarget() } } -ModelNode QmlFlowActionAreaNode::decisionNodeForTransition(const ModelNode &transition) const -{ - ModelNode target = targetTransition(); - - if (target.isValid() && target.hasMetaInfo() && QmlVisualNode::isFlowTransition(target)) { - - ModelNode finalTarget = target.bindingProperty("to").resolveToModelNode(); - - if (finalTarget.isValid() && finalTarget.hasMetaInfo() && QmlVisualNode::isFlowDecision(finalTarget)) { - if (finalTarget.hasBindingProperty("targets") - && finalTarget.bindingProperty("targets").resolveToModelNodeList().contains(transition)) - return finalTarget; - } - QmlFlowViewNode flowView(view()->rootModelNode()); - if (flowView.isValid()) { - for (const ModelNode target : flowView.decicions()) { - if (target.hasBindingProperty("targets") - && target.bindingProperty("targets").resolveToModelNodeList().contains(transition)) - return target; - } - } - } - - return {}; -} - bool QmlFlowViewNode::isValid() const { return isValidQmlFlowViewNode(modelNode()); From 270e7b4ea5889903383db30ff2614b7529a0ac76 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sat, 13 Jun 2020 20:55:36 +0200 Subject: [PATCH 15/46] UnitTests: Fix fake project.h Change-Id: I5bd3be88c900af0caaccf8ca3ba2793f20ccf6f8 Reviewed-by: Christian Kandeler --- tests/unit/mockup/projectexplorer/project.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/mockup/projectexplorer/project.h b/tests/unit/mockup/projectexplorer/project.h index e193ad404d7..9e8e6a53115 100644 --- a/tests/unit/mockup/projectexplorer/project.h +++ b/tests/unit/mockup/projectexplorer/project.h @@ -44,7 +44,7 @@ public: Target *activeTarget() const { return {}; } - QVariant namedSettings(const QString &name) const { return settings.at(name); } + QVariant namedSettings(const QString &name) const { return settings[name]; } void setNamedSettings(const QString &name, const QVariant &value) { settings[name] = value; } Utils::FilePath rootProjectDirectoryPath; From 86cf85af5e652a96385bba56b11c3a2f17f7a023 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 12 Jun 2020 10:29:48 +0200 Subject: [PATCH 16/46] qmlpuppet: improve text Change-Id: Ibd1b7cd783561bc0b1e853e8875ee631eddb85d6 Reviewed-by: Tim Jenssen --- share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri index a2b3219327b..f772fd8a556 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri @@ -5,9 +5,11 @@ CONFIG += c++11 DEFINES -= QT_CREATOR -# This .pri file contains classes to enable a special multilanguage translator debug service +# This .pri file contains classes to enable a special multilanguage translator MULTILANGUAGE_SUPPORT_PRI=$$(MULTILANGUAGE_SUPPORT_PRI) !isEmpty(MULTILANGUAGE_SUPPORT_PRI) { + exists($$(MULTILANGUAGE_SUPPORT_PRI)): message(including \"$$(MULTILANGUAGE_SUPPORT_PRI)\") + else: error("MULTILANGUAGE_SUPPORT_PRI: \"$$(MULTILANGUAGE_SUPPORT_PRI)\" does not exist.") include($$(MULTILANGUAGE_SUPPORT_PRI)) DEFINES += MULTILANGUAGE_TRANSLATIONPROVIDER } From dd8709e5546211976a97aa561d6c97cea4d233bb Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 16 Jun 2020 10:50:31 +0200 Subject: [PATCH 17/46] AssetExporter: Add plugin to cmake and qbs build system Change-Id: I2b48a854ed4e5e94cab73883f8b0f41d0ed15697 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 19 +++++++ .../assetexporterplugin.qbs | 51 +++++++++++++++++++ src/plugins/qmldesigner/qmldesigner.qbs | 1 + 3 files changed, 71 insertions(+) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 345c756034b..e3d52ba8639 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -38,6 +38,25 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() + +add_qtc_plugin(assetexporterplugin + CONDITION TARGET QmlDesigner + DEPENDS Core ProjectExplorer QmlDesigner Utils Qt5::Qml + PUBLIC_INCLUDES assetexporterplugin + SOURCES + assetexporterplugin/assetexportdialog.h assetexporterplugin/assetexportdialog.cpp assetexporterplugin/assetexportdialog.ui + assetexporterplugin/assetexporter.h assetexporterplugin/assetexporter.cpp + assetexporterplugin/assetexporterplugin.h assetexporterplugin/assetexporterplugin.cpp + assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp + assetexporterplugin/assetexportpluginconstants.h + assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp + assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp + assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp + assetexporterplugin/assetexporterplugin.qrc + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} + SKIP_DEBUG_CMAKE_FILE_CHECK +) + add_qtc_plugin(componentsplugin CONDITION TARGET QmlDesigner DEPENDS Core QmlDesigner Utils Qt5::Qml diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs new file mode 100644 index 00000000000..b6cd9ddfc37 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -0,0 +1,51 @@ +import qbs + +QtcProduct { + name: "assetexporterplugin" + type: ["dynamiclibrary"] + installDir: qtc.ide_plugin_path + '/' + installDirName + property string installDirName: qbs.targetOS.contains("macos") ? "QmlDesigner" : "qmldesigner" + + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } + Depends { name: "QmlDesigner" } + Depends { name: "Utils" } + + cpp.includePaths: base.concat([ + "./", + "../designercore/include", + "../../../../share/qtcreator/qml/qmlpuppet/interfaces", + "../../../../share/qtcreator/qml/qmlpuppet/types" + ]) + + Properties { + condition: qbs.targetOS.contains("unix") + cpp.internalVersion: "" + } + + Group { + name: "plugin metadata" + files: ["assetexporterplugin.json"] + fileTags: ["qt_plugin_metadata"] + } + + files: [ + "assetexportdialog.cpp", + "assetexportdialog.h", + "assetexportdialog.ui", + "assetexporter.cpp", + "assetexporter.h", + "assetexporterplugin.cpp", + "assetexporterplugin.h", + "assetexporterplugin.qrc", + "assetexporterview.cpp", + "assetexporterview.h", + "assetexportpluginconstants.h", + "componentexporter.cpp", + "componentexporter.h", + "parsers/modelitemnodeparser.cpp", + "parsers/modelitemnodeparser.h", + "parsers/modelnodeparser.cpp", + "parsers/modelnodeparser.h" + ] +} diff --git a/src/plugins/qmldesigner/qmldesigner.qbs b/src/plugins/qmldesigner/qmldesigner.qbs index e46eec6f912..b14db788660 100644 --- a/src/plugins/qmldesigner/qmldesigner.qbs +++ b/src/plugins/qmldesigner/qmldesigner.qbs @@ -6,5 +6,6 @@ Project { "qmldesignerplugin.qbs", "qtquickplugin/qtquickplugin.qbs", "componentsplugin/componentsplugin.qbs", + "assetexporterplugin/assetexporterplugin.qbs" ] } From 2ed7a7963e16021dd6ce1e8e83f676cf2d751570 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 15 Jun 2020 22:20:51 +0200 Subject: [PATCH 18/46] qmldesigner: block local changes while initialization - emit after the initialization - emit only if found languages are differrent Change-Id: I4f426b7bf055ef1d97e5db86bba075a032210303 Reviewed-by: Tim Jenssen --- .../qmlpreviewplugin/qmlpreviewactions.cpp | 43 +++++++++++-------- .../qmlpreviewplugin/qmlpreviewactions.h | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp index 32d524f6909..c0da2f8b8c6 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp @@ -217,42 +217,46 @@ SwitchLanguageComboboxAction::SwitchLanguageComboboxAction(QObject *parent) connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::startupProjectChanged, this, - &SwitchLanguageComboboxAction::refreshProjectLocales); + &SwitchLanguageComboboxAction::updateProjectLocales); } QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent) { QPointer comboBox = new QComboBox(parent); comboBox->setToolTip(tr("Switch the language used by preview.")); + comboBox->addItem(tr("Default")); + + auto refreshComboBoxFunction = [this, comboBox] (ProjectExplorer::Project *project) { + if (comboBox) { + if (updateProjectLocales(project)) { + comboBox->clear(); + comboBox->addItem(tr("Default")); + comboBox->addItems(m_localeStrings); + } + } + }; + connect(ProjectExplorer::SessionManager::instance(), &ProjectExplorer::SessionManager::startupProjectChanged, + comboBox, refreshComboBoxFunction); + + if (auto project = SessionManager::startupProject()) + refreshComboBoxFunction(project); + + // do this after refreshComboBoxFunction so we do not get currentLocaleChanged signals at initialization connect(comboBox, QOverload::of(&QComboBox::currentIndexChanged), [this, comboBox](int index) { - if (index == 0) + if (index == 0) // == Default emit currentLocaleChanged(""); else emit currentLocaleChanged(comboBox->currentText()); }); - auto refreshComboBoxFunction = [this, comboBox] (ProjectExplorer::Project *project) { - if (comboBox) { - refreshProjectLocales(project); - comboBox->clear(); - comboBox->addItem(tr("Default")); - comboBox->addItems(m_localeStrings); - } - }; - connect(ProjectExplorer::SessionManager::instance(), - &ProjectExplorer::SessionManager::startupProjectChanged, - refreshComboBoxFunction); - - if (auto project = SessionManager::startupProject()) - refreshComboBoxFunction(project); - return comboBox; } -void SwitchLanguageComboboxAction::refreshProjectLocales(Project *project) +bool SwitchLanguageComboboxAction::updateProjectLocales(Project *project) { if (!project) - return; + return false; + auto previousLocales = m_localeStrings; m_localeStrings.clear(); const auto projectDirectory = project->rootProjectDirectory().toFileInfo().absoluteFilePath(); const QDir languageDirectory(projectDirectory + "/i18n"); @@ -263,6 +267,7 @@ void SwitchLanguageComboboxAction::refreshProjectLocales(Project *project) const QString locale = qmFile.left(localeEndPosition).mid(localeStartPosition); return locale; }); + return previousLocales != m_localeStrings; } SwitchLanguageAction::SwitchLanguageAction() diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h index 8a71ee07c29..a23f125ca14 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h @@ -113,7 +113,7 @@ signals: protected: QWidget *createWidget(QWidget *parent) override; private: - void refreshProjectLocales(ProjectExplorer::Project *project); + bool updateProjectLocales(ProjectExplorer::Project *project); QStringList m_localeStrings; }; From aaf51d1cfbf26bac09ea670291a0c0003dfc8a8e Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 18 Jun 2020 11:53:05 +0200 Subject: [PATCH 19/46] qmldesigner: no need to call it two times Change-Id: Ib25f65bdc182fefa14f88a21154a6d3481c9716f Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/qmldesignerplugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 21d1ffadf22..e7d3e0a23d5 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -450,7 +450,6 @@ void QmlDesignerPlugin::activateAutoSynchronization() if (!currentDesignDocument()->isDocumentLoaded()) currentDesignDocument()->loadDocument(currentDesignDocument()->plainTextEdit()); - currentDesignDocument()->updateActiveTarget(); currentDesignDocument()->updateActiveTarget(); d->mainWidget.enableWidgets(); currentDesignDocument()->attachRewriterToModel(); From 52316578f837ca38c6631222808af4288affe40f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 18 Jun 2020 08:49:29 +0200 Subject: [PATCH 20/46] qml: introduce QmlMultiLanguageAspect This aspect is only available if the special multilanguage plugin is available. Translation changes in that plugin changes this aspect, which results in environment variables which activate the use of a multilanguage created sqlite to provide the translations. Change-Id: I38250b69165eb7ec3e4f82dc34b3cc4ba0a33f8f Reviewed-by: hjk Reviewed-by: Tim Jenssen --- .../instances/nodeinstanceview.cpp | 20 ++- .../designercore/instances/puppetcreator.cpp | 33 ++--- .../designercore/instances/puppetcreator.h | 1 - src/plugins/qmldesigner/designersettings.cpp | 1 - src/plugins/qmldesigner/designersettings.h | 1 - .../qmldesigner/qmldesigner_dependencies.pri | 1 + .../qmlpreviewplugin/qmlpreviewactions.cpp | 6 +- .../qmlpreview/qmlpreviewruncontrol.cpp | 8 +- src/plugins/qmlprojectmanager/CMakeLists.txt | 1 + .../qmlmultilanguageaspect.cpp | 128 ++++++++++++++++++ .../qmlmultilanguageaspect.h | 57 ++++++++ .../qmlprojectmanager/qmlprojectmanager.pro | 2 + .../qmlprojectmanager/qmlprojectmanager.qbs | 1 + .../qmlprojectmanagerconstants.h | 2 + .../qmlprojectrunconfiguration.cpp | 55 +++++--- 15 files changed, 264 insertions(+), 53 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp create mode 100644 src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 96dd127f28d..d87bc128d2b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -83,6 +83,10 @@ #include #endif +#include + +#include + #include #include @@ -542,7 +546,12 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, } } else if (node.isRootNode() && name == "language@Internal") { const QString languageAsString = value.toString(); - DesignerSettings::setValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE, languageAsString); + if (m_currentTarget) { + if (auto rc = m_currentTarget->activeRunConfiguration()) { + if (auto multiLanguageAspect = rc->aspect()) + multiLanguageAspect->setLastUsedLanguage(languageAsString); + } + } nodeInstanceServer()->changeLanguage({languageAsString}); } else if (node.isRootNode() && name == "previewSize@Internal") { nodeInstanceServer()->changePreviewImageSize(value.toSize()); @@ -984,6 +993,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() } } + QString lastUsedLanguage; + if (m_currentTarget) { + if (auto rc = m_currentTarget->activeRunConfiguration()) { + if (auto multiLanguageAspect = rc->aspect()) + lastUsedLanguage = multiLanguageAspect->lastUsedLanguage(); + } + } return CreateSceneCommand( instanceContainerList, @@ -996,7 +1012,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() mockupTypesVector, model()->fileUrl(), m_edit3DToolStates[model()->fileUrl()], - DesignerSettings::getValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE).toString() + lastUsedLanguage ); } diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index a33d7f1f5a0..943e21e6f7f 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -36,17 +36,20 @@ #include +#include +#include + #include #include #include #include #include -#include -#include + +#include + #include #include #include -#include #include #include @@ -162,18 +165,6 @@ QString PuppetCreator::getStyleConfigFileName() const return QString(); } -QString PuppetCreator::getMultilanguageDatabaseFilePath() const -{ -#ifndef QMLDESIGNER_TEST - if (m_target) { - auto filePath = m_target->project()->projectDirectory().pathAppended("/multilanguage-experimental-v1.db"); - if (filePath.exists()) - return filePath.toString(); - } -#endif - return {}; -} - PuppetCreator::PuppetCreator(ProjectExplorer::Target *target, const Model *model) : m_target(target) @@ -494,11 +485,6 @@ QProcessEnvironment PuppetCreator::processEnvironment() const environment.set("QMLDESIGNER_RC_PATHS", m_qrcMapping); } - const QString multilanguageDatabaseFilePath = getMultilanguageDatabaseFilePath(); - - if (!multilanguageDatabaseFilePath.isEmpty()) - environment.set("QT_MULTILANGUAGE_DATABASE", multilanguageDatabaseFilePath); - #ifndef QMLDESIGNER_TEST auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); view->emitCustomNotification("PuppetStatus", {}, {QVariant(m_qrcMapping)}); @@ -525,6 +511,13 @@ QProcessEnvironment PuppetCreator::processEnvironment() const importPaths.append(designerImports); customFileSelectors = m_target->additionalData("CustomFileSelectorsData").toStringList(); + + if (auto *rc = m_target->activeRunConfiguration()) { + if (auto multiLanguageAspect = rc->aspect()) { + if (!multiLanguageAspect->databaseFilePath().isEmpty()) + environment.set("QT_MULTILANGUAGE_DATABASE", multiLanguageAspect->databaseFilePath().toString()); + } + } } customFileSelectors.append("DesignMode"); diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h index f8033fd3e19..bafea8fa3e6 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h @@ -102,7 +102,6 @@ protected: bool useOnlyFallbackPuppet() const; QString getStyleConfigFileName() const; - QString getMultilanguageDatabaseFilePath() const; private: mutable QString m_compileLog; diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index 96504f3a1cc..91091c53d66 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -85,7 +85,6 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::SIMPLE_COLOR_PALETTE_CONTENT, QStringList()); restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); restoreValue(settings, DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER, true); - restoreValue(settings, DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE, "en"); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index 746465386d7..42bc957b96c 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -69,7 +69,6 @@ const char ENABLE_TIMELINEVIEW[] = "EnableTimelineView"; const char SIMPLE_COLOR_PALETTE_CONTENT[] = "SimpleColorPaletteContent"; const char ALWAYS_DESIGN_MODE[] = "AlwaysDesignMode"; const char DISABLE_ITEM_LIBRARY_UPDATE_TIMER[] = "DisableItemLibraryUpdateTimer"; -const char LAST_USED_TRANSLATION_LANGUAGE[] = "LastUsedTranslationLanguage"; } class QMLDESIGNERCORE_EXPORT DesignerSettings : public QHash diff --git a/src/plugins/qmldesigner/qmldesigner_dependencies.pri b/src/plugins/qmldesigner/qmldesigner_dependencies.pri index cba0186ce73..321d2c2b071 100644 --- a/src/plugins/qmldesigner/qmldesigner_dependencies.pri +++ b/src/plugins/qmldesigner/qmldesigner_dependencies.pri @@ -11,6 +11,7 @@ QTC_PLUGIN_DEPENDS += \ qtsupport \ projectexplorer \ qmakeprojectmanager \ + qmlprojectmanager \ resourceeditor INCLUDEPATH *= \ diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp index c0da2f8b8c6..190d52b349d 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp @@ -48,12 +48,10 @@ const Utils::Icon previewIcon({ static void handleAction(const SelectionContext &context) { if (context.view()->isAttached()) { - if (context.toggled()) { - QmlPreviewPlugin::setLanguageLocale(DesignerSettings::getValue(DesignerSettingsKey::LAST_USED_TRANSLATION_LANGUAGE).toString()); + if (context.toggled()) ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE); - } else { + else QmlPreviewPlugin::stopAllRunControls(); - } } } diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index a6437d62f96..f1caffa1a7a 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -152,10 +153,9 @@ LocalQmlPreviewSupport::LocalQmlPreviewSupport(ProjectExplorer::RunControl *runC } } - if (runControl->project()) { - auto multilanguageDatabaseFilePath = runControl->project()->projectDirectory().pathAppended("/multilanguage-experimental-v1.db"); - if (multilanguageDatabaseFilePath.exists()) - runnable.environment.set("QT_MULTILANGUAGE_DATABASE", multilanguageDatabaseFilePath.toString()); + if (auto multiLanguageAspect = runControl->aspect()) { + if (!multiLanguageAspect->databaseFilePath().isEmpty()) + runnable.environment.set("QT_MULTILANGUAGE_DATABASE", multiLanguageAspect->databaseFilePath().toString()); } Utils::QtcProcess::addArg(&runnable.commandLineArguments, diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 1d8527c221a..fa037362a50 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -6,6 +6,7 @@ add_qtc_plugin(QmlProjectManager fileformat/qmlprojectfileformat.cpp fileformat/qmlprojectfileformat.h fileformat/qmlprojectitem.cpp fileformat/qmlprojectitem.h qmlmainfileaspect.cpp qmlmainfileaspect.h + qmlmultilanguageaspect.cpp qmlmultilanguageaspect.h qmlproject.cpp qmlproject.h qmlproject.qrc qmlprojectconstants.h diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp new file mode 100644 index 00000000000..64dd8a93d60 --- /dev/null +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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 "qmlmultilanguageaspect.h" + +#include +#include + +#include +#include +#include + +static bool isMultilanguagePresent() +{ + const QVector specs = ExtensionSystem::PluginManager::plugins(); + return std::find_if(specs.begin(), + specs.end(), + [](ExtensionSystem::PluginSpec *spec) { + return spec->name() == "MultiLanguage"; + }) + != specs.end(); +} + +static Utils::FilePath getMultilanguageDatabaseFilePath(ProjectExplorer::Target *target) +{ + if (target) { + auto filePath = target->project()->projectDirectory().pathAppended("/multilanguage-experimental-v1.db"); + if (filePath.exists()) + return filePath; + } + return {}; +} + +static QObject *getPreviewPlugin() +{ + auto pluginIt = std::find_if(ExtensionSystem::PluginManager::plugins().begin(), + ExtensionSystem::PluginManager::plugins().end(), + [](const ExtensionSystem::PluginSpec *p) { + return p->name() == "QmlPreview"; + }); + + if (pluginIt != ExtensionSystem::PluginManager::plugins().constEnd()) + return (*pluginIt)->plugin(); + + return nullptr; +} + + +namespace QmlProjectManager { + +QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target) + : m_target(target) +{ + setVisible(isMultilanguagePresent()); + setSettingsKey(Constants::USE_MULTILANGUAGE_KEY); + setLabel(tr("Use MultiLanguage translation database."), BaseBoolAspect::LabelPlacement::AtCheckBox); + setToolTip(tr("Enable loading application with special desktop SQLite translation database.")); + + setDefaultValue(!databaseFilePath().isEmpty()); + QVariantMap getDefaultValues; + fromMap(getDefaultValues); + + if (auto previewPlugin = getPreviewPlugin()) + connect(previewPlugin, SIGNAL(localeChanged(QString)), this, SLOT(setLastUsedLanguage(QString))); +} + +QmlMultiLanguageAspect::~QmlMultiLanguageAspect() +{ +} + +void QmlMultiLanguageAspect::setLastUsedLanguage(const QString &language) +{ + if (auto previewPlugin = getPreviewPlugin()) + previewPlugin->setProperty("locale", language); + if (m_lastUsedLanguage != language) { + m_lastUsedLanguage = language; + emit changed(); + } +} + +QString QmlMultiLanguageAspect::lastUsedLanguage() const +{ + return m_lastUsedLanguage; +} + +Utils::FilePath QmlMultiLanguageAspect::databaseFilePath() const +{ + if (m_databaseFilePath.isEmpty()) + m_databaseFilePath = getMultilanguageDatabaseFilePath(m_target); + return m_databaseFilePath; +} + +void QmlMultiLanguageAspect::toMap(QVariantMap &map) const +{ + BaseBoolAspect::toMap(map); + if (!m_lastUsedLanguage.isEmpty()) + map.insert(Constants::LAST_USED_LANGUAGE, m_lastUsedLanguage); +} + +void QmlMultiLanguageAspect::fromMap(const QVariantMap &map) +{ + BaseBoolAspect::fromMap(map); + setLastUsedLanguage(map.value(Constants::LAST_USED_LANGUAGE, "en").toString()); +} + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h new file mode 100644 index 00000000000..163552caf0d --- /dev/null +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "qmlprojectmanager_global.h" + +#include + +#include + +namespace QmlProjectManager { + +class QMLPROJECTMANAGER_EXPORT QmlMultiLanguageAspect : public ProjectExplorer::BaseBoolAspect +{ + Q_OBJECT +public: + explicit QmlMultiLanguageAspect(ProjectExplorer::Target *target); + ~QmlMultiLanguageAspect() override; + + QString lastUsedLanguage() const; + Utils::FilePath databaseFilePath() const; + void toMap(QVariantMap &map) const final; + void fromMap(const QVariantMap &map) final; + +public slots: + void setLastUsedLanguage(const QString &language); + +private: + ProjectExplorer::Target *m_target = nullptr; + mutable Utils::FilePath m_databaseFilePath; + QString m_lastUsedLanguage; +}; + +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro index 049c5a801b7..330d93d6012 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro @@ -7,6 +7,7 @@ DEFINES += QMLPROJECTMANAGER_LIBRARY HEADERS += \ qmlmainfileaspect.h \ + qmlmultilanguageaspect.h \ qmlproject.h \ qmlprojectplugin.h \ qmlprojectconstants.h \ @@ -17,6 +18,7 @@ HEADERS += \ SOURCES += \ qmlmainfileaspect.cpp \ + qmlmultilanguageaspect.cpp \ qmlproject.cpp \ qmlprojectplugin.cpp \ qmlprojectnodes.cpp \ diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index 8d466ab2621..1a160bff1cd 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -16,6 +16,7 @@ QtcPlugin { name: "General" files: [ "qmlmainfileaspect.cpp", "qmlmainfileaspect.h", + "qmlmultilanguageaspect.cpp", "qmlmultilanguageaspect.h", "qmlproject.cpp", "qmlproject.h", "qmlproject.qrc", "qmlprojectconstants.h", diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanagerconstants.h b/src/plugins/qmlprojectmanager/qmlprojectmanagerconstants.h index cddac079dc8..2abdb9fa34b 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanagerconstants.h +++ b/src/plugins/qmlprojectmanager/qmlprojectmanagerconstants.h @@ -34,6 +34,8 @@ const char QML_PROJECT_ID[] = "QmlProjectManager.QmlProject"; const char QML_VIEWER_ARGUMENTS_KEY[] = "QmlProjectManager.QmlRunConfiguration.QDeclarativeViewerArguments"; const char QML_VIEWER_TARGET_DISPLAY_NAME[] = "QML Viewer"; const char QML_MAINSCRIPT_KEY[] = "QmlProjectManager.QmlRunConfiguration.MainScript"; +const char USE_MULTILANGUAGE_KEY[] = "QmlProjectManager.QmlRunConfiguration.UseMultiLanguage"; +const char LAST_USED_LANGUAGE[] = "QmlProjectManager.QmlRunConfiguration.LastUsedLanguage"; const char USER_ENVIRONMENT_CHANGES_KEY[] = "QmlProjectManager.QmlRunConfiguration.UserEnvironmentChanges"; } // namespace Constants diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 5afb957789c..8ec0c29246f 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -27,7 +27,7 @@ #include "qmlproject.h" #include "qmlprojectmanagerconstants.h" #include "qmlmainfileaspect.h" -#include "qmlmainfileaspect.h" +#include "qmlmultilanguageaspect.h" #include #include @@ -59,6 +59,7 @@ using namespace QtSupport; using namespace Utils; namespace QmlProjectManager { +class QmlMultiLanguageAspect; namespace Internal { // QmlProjectRunConfiguration @@ -81,30 +82,12 @@ private: BaseStringAspect *m_qmlViewerAspect = nullptr; QmlMainFileAspect *m_qmlMainFileAspect = nullptr; + QmlMultiLanguageAspect *m_multiLanguageAspect = nullptr; }; QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect(); - - auto envModifier = [this](Environment env) { - if (auto bs = dynamic_cast(activeBuildSystem())) - env.modify(bs->environment()); - return env; - }; - - const Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(target->kit()); - if (deviceTypeId == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - envAspect->addPreferredBaseEnvironment(tr("System Environment"), [envModifier] { - return envModifier(Environment::systemEnvironment()); - }); - } - - envAspect->addSupportedBaseEnvironment(tr("Clean Environment"), [envModifier] { - return envModifier(Environment()); - }); - m_qmlViewerAspect = addAspect(); m_qmlViewerAspect->setLabelText(tr("QML Viewer:")); m_qmlViewerAspect->setPlaceHolderText(commandLine().executable().toString()); @@ -123,6 +106,34 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) connect(target, &Target::kitChanged, this, &RunConfiguration::update); + m_multiLanguageAspect = addAspect(target); + + auto envAspect = addAspect(); + connect(m_multiLanguageAspect, &QmlMultiLanguageAspect::changed, envAspect, &EnvironmentAspect::environmentChanged); + + auto envModifier = [this](Environment env) { + if (auto bs = dynamic_cast(activeBuildSystem())) + env.modify(bs->environment()); + + if (m_multiLanguageAspect && m_multiLanguageAspect->value() && !m_multiLanguageAspect->databaseFilePath().isEmpty()) { + env.set("QT_MULTILANGUAGE_DATABASE", m_multiLanguageAspect->databaseFilePath().toString()); + env.set("QT_MULTILANGUAGE_LANGUAGE", m_multiLanguageAspect->lastUsedLanguage()); + } + return env; + }; + + const Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(target->kit()); + if (deviceTypeId == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { + envAspect->addPreferredBaseEnvironment(tr("System Environment"), [envModifier] { + return envModifier(Environment::systemEnvironment()); + }); + } + + envAspect->addSupportedBaseEnvironment(tr("Clean Environment"), [envModifier] { + Environment environment; + return envModifier(environment); + }); + setDisplayName(tr("QML Scene", "QMLRunConfiguration display name.")); update(); } @@ -208,6 +219,10 @@ QString QmlProjectRunConfiguration::commandLineArguments() const const QString main = bs->targetFile(FilePath::fromString(mainScript())).toString(); if (!main.isEmpty()) QtcProcess::addArg(&args, main, osType); + + if (m_multiLanguageAspect && m_multiLanguageAspect->value()) + QtcProcess::addArg(&args, "-qmljsdebugger=file:unused_if_debugger_arguments_added,services:DebugTranslation", osType); + return args; } From e1e7a1786fdba2dc0aac6cb2d9ad6d3b7688678c Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 18 Jun 2020 08:50:58 +0200 Subject: [PATCH 21/46] qmldebug: add DebugTranslation If the used Qt does not provide this service it will be automatically ignored. Change-Id: I2f07751c579b13d37349384d9150da9c1c13f092 Reviewed-by: Tim Jenssen --- src/libs/qmldebug/qmldebugcommandlinearguments.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/qmldebug/qmldebugcommandlinearguments.h b/src/libs/qmldebug/qmldebugcommandlinearguments.h index 0a22d0f4c94..f69fec2f9bd 100644 --- a/src/libs/qmldebug/qmldebugcommandlinearguments.h +++ b/src/libs/qmldebug/qmldebugcommandlinearguments.h @@ -47,11 +47,11 @@ inline QString qmlDebugServices(QmlDebugServicesPreset preset) case NoQmlDebugServices: return QString(); case QmlDebuggerServices: - return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector"); + return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector,DebugTranslation"); case QmlProfilerServices: - return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages"); + return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages,DebugTranslation"); case QmlNativeDebuggerServices: - return QStringLiteral("NativeQmlDebugger"); + return QStringLiteral("NativeQmlDebugger,DebugTranslation"); case QmlPreviewServices: return QStringLiteral("QmlPreview,DebugTranslation"); default: From 7f1b28626406eb7da39f543181f142a95d9d46c6 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 11 Jun 2020 19:56:53 +0200 Subject: [PATCH 22/46] AssetExporter: Add import notification task Task-number: QDS-1560 Change-Id: I09c14c22206c299f7a50f1a843c6dc4ebac670e2 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetexporterplugin/assetexporter.cpp | 16 +++-- .../assetexporterplugin.cpp | 4 ++ .../assetexporterplugin.pri | 2 + .../assetexporterplugin.qbs | 2 + .../assetexportpluginconstants.h | 2 + .../exportnotification.cpp | 58 +++++++++++++++++++ .../assetexporterplugin/exportnotification.h | 33 +++++++++++ 8 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/exportnotification.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index e3d52ba8639..7a38eb4c014 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -50,6 +50,7 @@ add_qtc_plugin(assetexporterplugin assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp assetexporterplugin/assetexportpluginconstants.h assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp + assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp assetexporterplugin/assetexporterplugin.qrc diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index b667584529f..812898e378d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "assetexporter.h" #include "componentexporter.h" +#include "exportnotification.h" #include "plaintexteditmodifier.h" #include "rewriterview.h" @@ -134,6 +135,9 @@ bool AssetExporter::preProcessProject() void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets) { + ExportNotification::addInfo(tr("Exporting metadata at %1. Export assets: ") + .arg(exportPath.toUserOutput()) + .arg(exportAssets? tr("Yes") : tr("No"))); // TODO Asset export Q_UNUSED(exportAssets); m_exportFiles = qmlFiles; @@ -145,6 +149,7 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil void AssetExporter::cancel() { + ExportNotification::addInfo("Cancelling export."); if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && !m_preprocessWatcher->isFinished()) { m_preprocessWatcher->cancel(); @@ -181,8 +186,8 @@ void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) default: return; } - // TODO. Communicate errors to user qCDebug(loggerError) << "QML load error:" << errorStr; + ExportNotification::addError(tr("Loading QML failed. %1").arg(errorStr)); } void AssetExporter::onQmlFileLoaded() @@ -208,24 +213,27 @@ void AssetExporter::loadNextFile() // Load the next pending file. const Utils::FilePath file = m_exportFiles.takeFirst(); + ExportNotification::addInfo(tr("Exporting file %1.").arg(file.toUserOutput())); qCDebug(loggerInfo) << "Loading next file" << file; m_view->loadQmlFile(file); } void AssetExporter::writeMetadata() const { - qCDebug(loggerInfo) << "Writing metadata"; + ExportNotification::addInfo(tr("Writing metadata to file %1."). + arg(m_exportPath.toUserOutput())); m_currentState.change(ParsingState::WritingJson); QJsonObject jsonRoot; // TODO: Write plugin info to root jsonRoot.insert("artboards", m_components); QJsonDocument doc(jsonRoot); if (doc.isNull() || doc.isEmpty()) { - qCDebug(loggerWarn) << "Empty JSON document"; + ExportNotification::addError(tr("Empty JSON document.")); } else { Utils::FileSaver saver(m_exportPath.toString(), QIODevice::Text); saver.write(doc.toJson(QJsonDocument::Indented)); if (!saver.finalize()) { - qCDebug(loggerError) << "Cannot write Metadata file: " << saver.errorString(); + ExportNotification::addError(tr("Writing metadata failed. %1"). + arg(saver.errorString())); } } m_currentState.change(ParsingState::ExportingDone); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index e23b5168289..6ac8906946f 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -41,6 +41,7 @@ #include "projectexplorer/session.h" #include "projectexplorer/project.h" #include "projectexplorer/session.h" +#include "projectexplorer/taskhub.h" #include "extensionsystem/pluginmanager.h" #include "extensionsystem/pluginspec.h" @@ -57,6 +58,9 @@ namespace QmlDesigner { AssetExporterPlugin::AssetExporterPlugin() : m_view(new AssetExporterView) { + ProjectExplorer::TaskHub::addCategory( Constants::TASK_CATEGORY_ASSET_EXPORT, + tr("Asset Export"), false); + auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); auto &viewManager = designerPlugin->viewManager(); viewManager.registerViewTakingOwnership(m_view); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri index 17027e9776e..8c8c5126599 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -13,6 +13,7 @@ HEADERS += \ assetexporterview.h \ assetexportpluginconstants.h \ componentexporter.h \ + exportnotification.h \ parsers/modelitemnodeparser.h \ parsers/modelnodeparser.h @@ -22,6 +23,7 @@ SOURCES += \ assetexporterplugin.cpp \ assetexporterview.cpp \ componentexporter.cpp \ + exportnotification.cpp \ parsers/modelitemnodeparser.cpp \ parsers/modelnodeparser.cpp diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs index b6cd9ddfc37..538d7b19802 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -43,6 +43,8 @@ QtcProduct { "assetexportpluginconstants.h", "componentexporter.cpp", "componentexporter.h", + "exportnotification.cpp", + "exportnotification.h", "parsers/modelitemnodeparser.cpp", "parsers/modelitemnodeparser.h", "parsers/modelnodeparser.cpp", diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 8432e6b8403..79145bb1456 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -29,5 +29,7 @@ namespace Constants { const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml"; +const char TASK_CATEGORY_ASSET_EXPORT[] = "AssetExporter.Export"; + } } diff --git a/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp new file mode 100644 index 00000000000..d4259c3fe20 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 Asset Importer 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 "exportnotification.h" +#include "assetexportpluginconstants.h" + +#include "projectexplorer/taskhub.h" + +#include + +namespace { +Q_LOGGING_CATEGORY(loggerDebug, "qtc.designer.assetExportPlugin.exportNotification", QtDebugMsg) +} + +using namespace ProjectExplorer; +namespace { +static void addTask(Task::TaskType type, const QString &desc) +{ + qCDebug(loggerDebug) << desc; + Task task(type, desc, {}, -1, QmlDesigner::Constants::TASK_CATEGORY_ASSET_EXPORT); + TaskHub::addTask(task); +} +} + +namespace QmlDesigner { + +void ExportNotification::addError(const QString &errMsg) +{ + addTask(Task::Error, errMsg); +} + +void ExportNotification::addWarning(const QString &warningMsg) +{ + addTask(Task::Warning, warningMsg); +} + +void ExportNotification::addInfo(const QString &infoMsg) +{ + addTask(Task::Unknown, infoMsg); +} +} // QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h new file mode 100644 index 00000000000..23fab1b8fa6 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** 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 Asset Importer 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 ExportNotification +{ +public: + static void addError(const QString &errMsg); + static void addWarning(const QString &warningMsg); + static void addInfo(const QString &infoMsg); +}; +} // QmlDesigner From 1b6a0a290bf2896d4b25fbb3f95aa774cfa30ff7 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Tue, 9 Jun 2020 00:23:11 +0200 Subject: [PATCH 23/46] AssetExporter: Let user select files to export Refine asset exporter dialog. Add progress bar, export notification, path chooser and other small UI changes Task-number: QDS-1560 Change-Id: Iaba61575581171e7e1495ebfaf2e345546fb7400 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetexporterplugin/assetexportdialog.cpp | 127 ++++++++++++-- .../assetexporterplugin/assetexportdialog.h | 22 ++- .../assetexporterplugin/assetexportdialog.ui | 41 ++++- .../assetexporterplugin/assetexporter.cpp | 98 ++--------- .../assetexporterplugin/assetexporter.h | 11 +- .../assetexporterplugin.cpp | 9 +- .../assetexporterplugin.pri | 2 + .../assetexporterplugin.qbs | 2 + .../assetexporterplugin/filepathmodel.cpp | 163 ++++++++++++++++++ .../assetexporterplugin/filepathmodel.h | 60 +++++++ 11 files changed, 411 insertions(+), 125 deletions(-) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7a38eb4c014..0e91850e82d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -51,6 +51,7 @@ add_qtc_plugin(assetexporterplugin assetexporterplugin/assetexportpluginconstants.h assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp + assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp assetexporterplugin/assetexporterplugin.qrc diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index f43b5a96b1d..ee5c9af64c1 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -23,44 +23,97 @@ ** ****************************************************************************/ #include "assetexportdialog.h" -#include "ui_assetexportdialog.h" +#include "ui_assetexportdialog.h" +#include "assetexportpluginconstants.h" +#include "filepathmodel.h" + +#include "projectexplorer/task.h" +#include "projectexplorer/taskhub.h" #include "utils/fileutils.h" +#include "utils/outputformatter.h" #include +#include +#include #include #include +#include #include -namespace QmlDesigner { +namespace { +static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str, + Utils::OutputFormat format) { + if (!formatter) + return; + QPlainTextEdit *edit = formatter->plainTextEdit(); + QScrollBar *scroll = edit->verticalScrollBar(); + bool isAtBottom = scroll && scroll->value() == scroll->maximum(); + + QString msg = str + "\n"; + formatter->appendMessage(msg, format); + + if (isAtBottom) + scroll->setValue(scroll->maximum()); +} +} + +using namespace ProjectExplorer; + +namespace QmlDesigner { AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, - AssetExporter &assetExporter, QWidget *parent) : + AssetExporter &assetExporter, FilePathModel &model, + QWidget *parent) : QDialog(parent), m_assetExporter(assetExporter), + m_filePathModel(model), m_ui(new Ui::AssetExportDialog), - m_model(this) + m_filesView(new QListView), + m_exportLogs(new QPlainTextEdit), + m_outputFormatter(new Utils::OutputFormatter()) { m_ui->setupUi(this); m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); - m_ui->filesView->setModel(&m_model); + m_ui->stackedWidget->addWidget(m_filesView); + m_filesView->setModel(&m_filePathModel); + + m_exportLogs->setReadOnly(true); + m_outputFormatter->setPlainTextEdit(m_exportLogs); + m_ui->stackedWidget->addWidget(m_exportLogs); + switchView(false); + + connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() { + m_assetExporter.cancel(); + }); m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole); m_exportBtn->setEnabled(false); connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport); - m_ui->exportPathEdit->setText(exportPath.toString()); - - connect(&m_assetExporter, &AssetExporter::qmlFileResult, this, [this] (const Utils::FilePath &path) { - m_qmlFiles.append(path); - QStringList files = m_model.stringList(); - files.append(path.toString()); - m_model.setStringList(files); + connect(&m_filePathModel, &FilePathModel::modelReset, this, [this]() { + m_ui->exportProgress->setRange(0, 1000); + m_ui->exportProgress->setValue(0); + m_exportBtn->setEnabled(true); }); + connect(m_ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() { + close(); + }); + m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false); + + m_ui->exportPathEdit->setFileName(exportPath); + m_ui->exportPathEdit->setPromptDialogTitle(tr("Choose Export Path")); + connect(&m_assetExporter, &AssetExporter::stateChanged, this, &AssetExportDialog::onExportStateChanged); + connect(&m_assetExporter, &AssetExporter::exportProgressChanged, + this, &AssetExportDialog::updateExportProgress); + + connect(TaskHub::instance(), &TaskHub::taskAdded, this, &AssetExportDialog::onTaskAdded); + + m_ui->exportProgress->setRange(0,0); } AssetExportDialog::~AssetExportDialog() @@ -70,25 +123,61 @@ AssetExportDialog::~AssetExportDialog() void AssetExportDialog::onExport() { - m_assetExporter.exportQml(m_qmlFiles, - Utils::FilePath::fromString(m_ui->exportPathEdit->text())); + switchView(true); + + updateExportProgress(0.0); + TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT); + m_exportLogs->clear(); + + m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPathEdit->fileName()); } void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState) { switch (newState) { - case AssetExporter::ParsingState::PreProcessing: - m_model.setStringList({}); - break; case AssetExporter::ParsingState::ExportingDone: + m_exportBtn->setVisible(false); + m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true); QMessageBox::information(this, tr("QML Export"), tr("Done")); break; default: break; } - m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::PreProcessingFinished || - newState == AssetExporter::ParsingState::ExportingDone); + m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::ExportingDone); m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy()); } + +void AssetExportDialog::updateExportProgress(double value) +{ + value = std::max(0.0, std::min(1.0, value)); + m_ui->exportProgress->setValue(std::round(value * 1000)); +} + +void AssetExportDialog::switchView(bool showExportView) +{ + if (showExportView) + m_ui->stackedWidget->setCurrentWidget(m_exportLogs); + else + m_ui->stackedWidget->setCurrentWidget(m_filesView); +} + +void AssetExportDialog::onTaskAdded(const ProjectExplorer::Task &task) +{ + Utils::OutputFormat format = Utils::NormalMessageFormat; + if (task.category == Constants::TASK_CATEGORY_ASSET_EXPORT) { + switch (task.type) { + case ProjectExplorer::Task::Error: + format = Utils::StdErrFormat; + break; + case ProjectExplorer::Task::Warning: + format = Utils::StdOutFormat; + break; + default: + format = Utils::NormalMessageFormat; + } + addFormattedMessage(m_outputFormatter, task.description, format); + } +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h index bad4b081954..7bf68b6a748 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -34,13 +34,24 @@ QT_BEGIN_NAMESPACE class QPushButton; +class QListView; +class QPlainTextEdit; QT_END_NAMESPACE namespace Ui { class AssetExportDialog; } +namespace Utils { +class OutputFormatter; +} + +namespace ProjectExplorer { +class Task; +} + namespace QmlDesigner { +class FilePathModel; class AssetExportDialog : public QDialog { @@ -48,19 +59,24 @@ class AssetExportDialog : public QDialog public: explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter, - QWidget *parent = nullptr); + FilePathModel& model, QWidget *parent = nullptr); ~AssetExportDialog(); private: void onExport(); void onExportStateChanged(AssetExporter::ParsingState newState); + void updateExportProgress(double value); + void switchView(bool showExportView); + void onTaskAdded(const ProjectExplorer::Task &task); private: AssetExporter &m_assetExporter; + FilePathModel &m_filePathModel; std::unique_ptr m_ui; QPushButton *m_exportBtn = nullptr; - QStringListModel m_model; - Utils::FilePaths m_qmlFiles; + QListView *m_filesView = nullptr; + QPlainTextEdit *m_exportLogs = nullptr; + Utils::OutputFormatter *m_outputFormatter = nullptr; }; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui index afe83d257dd..fb45d9c19d2 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -14,21 +14,52 @@ Export QML - - + + - + + + + 0 + 0 + + + + Export path: + + - + + + + + + + 1000 + + + 0 + + + + - QDialogButtonBox::Cancel + QDialogButtonBox::Cancel|QDialogButtonBox::Close + + + Utils::PathChooser + QWidget +
utils/pathchooser.h
+ 1 +
+
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 812898e378d..609da3f80e2 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -26,21 +26,11 @@ #include "componentexporter.h" #include "exportnotification.h" -#include "plaintexteditmodifier.h" -#include "rewriterview.h" - -#include "projectexplorer/project.h" -#include "projectexplorer/projectnodes.h" - -#include "utils/runextensions.h" #include "utils/qtcassert.h" #include #include #include -#include - -#include using namespace ProjectExplorer; @@ -48,48 +38,6 @@ namespace { Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg) Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg) Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg) - -void findQmlFiles(QFutureInterface &f, const Project *project) -{ - if (!project && !f.isCanceled()) - f.reportFinished({}); - - int index = 0; - Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool { - if (f.isCanceled()) - return false; - Utils::FilePath path = node->filePath(); - bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper(); - if (isComponent && node->filePath().endsWith(".ui.qml")) - f.reportResult(path, index++); - return true; - }); - f.reportFinished(); -} - -//static QmlDesigner::Model* createModel(const Utils::FilePath &fileName) -//{ -// QmlDesigner::Model *model = QmlDesigner::Model::create("Item", 2, 7); - -// Utils::FileReader reader; -// QTC_ASSERT(reader.fetch(fileName.toString()), return nullptr); - -// auto textEdit = new QPlainTextEdit; -// textEdit->setPlainText(QString::fromUtf8(reader.data())); - -// auto modifier = new QmlDesigner::NotIndentingTextEditModifier(textEdit); -// modifier->setParent(model); - -// auto rewriterView = new QmlDesigner::RewriterView(QmlDesigner::RewriterView::Validate, model); -// rewriterView->setCheckSemanticErrors(false); -// rewriterView->setTextModifier(modifier); - -// model->attachView(rewriterView); - -// QTC_ASSERT(rewriterView->rootModelNode().isValid(), return nullptr); -// return model; -//} - } namespace QmlDesigner { @@ -109,29 +57,6 @@ AssetExporter::~AssetExporter() cancel(); } -bool AssetExporter::preProcessProject() -{ - if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && - !m_preprocessWatcher->isFinished()) { - qCDebug(loggerInfo) << "Previous pre-processing not finished."; - return false; - } - - m_currentState.change(ParsingState::PreProcessing); - m_preprocessWatcher.reset(new QFutureWatcher(this)); - connect(m_preprocessWatcher.get(), &QFutureWatcher::resultReadyAt, this, - [this](int index) { - emit qmlFileResult(m_preprocessWatcher->resultAt(index)); - }); - - connect(m_preprocessWatcher.get(), &QFutureWatcher::finished, this, - [this] () { m_currentState.change(ParsingState::PreProcessingFinished); }); - - QFuture f = Utils::runAsync(&findQmlFiles, m_project); - m_preprocessWatcher->setFuture(f); - return true; -} - void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets) { @@ -139,6 +64,7 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil .arg(exportPath.toUserOutput()) .arg(exportAssets? tr("Yes") : tr("No"))); // TODO Asset export + notifyProgress(0.0); Q_UNUSED(exportAssets); m_exportFiles = qmlFiles; m_components = QJsonArray(); @@ -149,18 +75,12 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil void AssetExporter::cancel() { - ExportNotification::addInfo("Cancelling export."); - if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && - !m_preprocessWatcher->isFinished()) { - m_preprocessWatcher->cancel(); - m_preprocessWatcher->waitForFinished(); - } + // TODO Cancel export } bool AssetExporter::isBusy() const { - return m_currentState == AssetExporter::ParsingState::PreProcessing || - m_currentState == AssetExporter::ParsingState::Parsing || + return m_currentState == AssetExporter::ParsingState::Parsing || m_currentState == AssetExporter::ParsingState::ExportingAssets || m_currentState == AssetExporter::ParsingState::WritingJson; } @@ -190,6 +110,11 @@ void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) ExportNotification::addError(tr("Loading QML failed. %1").arg(errorStr)); } +void AssetExporter::notifyProgress(double value) const +{ + emit exportProgressChanged(value); +} + void AssetExporter::onQmlFileLoaded() { QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return); @@ -206,6 +131,7 @@ void AssetExporter::triggerLoadNextFile() void AssetExporter::loadNextFile() { if (m_exportFiles.isEmpty()) { + notifyProgress(0.8); m_currentState.change(ParsingState::ParsingFinished); writeMetadata(); return; @@ -220,8 +146,9 @@ void AssetExporter::loadNextFile() void AssetExporter::writeMetadata() const { + Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata"); ExportNotification::addInfo(tr("Writing metadata to file %1."). - arg(m_exportPath.toUserOutput())); + arg(metadataPath.toUserOutput())); m_currentState.change(ParsingState::WritingJson); QJsonObject jsonRoot; // TODO: Write plugin info to root jsonRoot.insert("artboards", m_components); @@ -229,13 +156,14 @@ void AssetExporter::writeMetadata() const if (doc.isNull() || doc.isEmpty()) { ExportNotification::addError(tr("Empty JSON document.")); } else { - Utils::FileSaver saver(m_exportPath.toString(), QIODevice::Text); + Utils::FileSaver saver(metadataPath.toString(), QIODevice::Text); saver.write(doc.toJson(QJsonDocument::Indented)); if (!saver.finalize()) { ExportNotification::addError(tr("Writing metadata failed. %1"). arg(saver.errorString())); } } + notifyProgress(1.0); m_currentState.change(ParsingState::ExportingDone); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index 3f13e85123e..9c2212f5e23 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -27,7 +27,6 @@ #include "assetexporterview.h" #include "utils/fileutils.h" -#include #include #include @@ -51,8 +50,6 @@ public: enum class ParsingState { Idle = 0, - PreProcessing, - PreProcessingFinished, Parsing, ParsingFinished, ExportingAssets, @@ -65,21 +62,22 @@ public: QObject *parent = nullptr); ~AssetExporter(); - bool preProcessProject(); - void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets = false); + void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, + bool exportAssets = false); void cancel(); bool isBusy() const; signals: - void qmlFileResult(Utils::FilePath); void stateChanged(ParsingState); + void exportProgressChanged(double) const; private: ParsingState currentState() const { return m_currentState.m_state; } void exportComponent(const ModelNode &rootNode); void writeMetadata() const; void notifyLoadError(AssetExporterView::LoadState state); + void notifyProgress(double value) const; void triggerLoadNextFile(); void loadNextFile(); @@ -98,7 +96,6 @@ private: Utils::FilePaths m_exportFiles; Utils::FilePath m_exportPath; QJsonArray m_components; - std::unique_ptr> m_preprocessWatcher; }; QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index 6ac8906946f..1758201bf7a 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -29,6 +29,7 @@ #include "assetexportdialog.h" #include "assetexporter.h" #include "assetexporterview.h" +#include "filepathmodel.h" #include "componentexporter.h" #include "parsers/modelitemnodeparser.h" @@ -89,14 +90,10 @@ void AssetExporterPlugin::onExport() if (!startupProject) return; + FilePathModel model(startupProject); auto exportDir = startupProject->projectFilePath().parentDir(); - if (!exportDir.toFileInfo().isRoot()) - exportDir = exportDir.parentDir(); - auto defaultMetadataPath = exportDir.pathAppended(startupProject->displayName() + ".metadata"); - AssetExporter assetExporter(m_view, startupProject); - AssetExportDialog assetExporterDialog(defaultMetadataPath, assetExporter); - assetExporter.preProcessProject(); + AssetExportDialog assetExporterDialog(exportDir, assetExporter, model); assetExporterDialog.exec(); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri index 8c8c5126599..d4ff10ec165 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -14,6 +14,7 @@ HEADERS += \ assetexportpluginconstants.h \ componentexporter.h \ exportnotification.h \ + filepathmodel.h \ parsers/modelitemnodeparser.h \ parsers/modelnodeparser.h @@ -24,6 +25,7 @@ SOURCES += \ assetexporterview.cpp \ componentexporter.cpp \ exportnotification.cpp \ + filepathmodel.cpp \ parsers/modelitemnodeparser.cpp \ parsers/modelnodeparser.cpp diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs index 538d7b19802..56c7659e102 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -45,6 +45,8 @@ QtcProduct { "componentexporter.h", "exportnotification.cpp", "exportnotification.h", + "filepathmodel.cpp", + "filepathmodel.h", "parsers/modelitemnodeparser.cpp", "parsers/modelitemnodeparser.h", "parsers/modelnodeparser.cpp", diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp new file mode 100644 index 00000000000..36c175414b5 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** 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 "filepathmodel.h" + +#include "exportnotification.h" + +#include "projectexplorer/project.h" +#include "projectexplorer/projectnodes.h" +#include "utils/runextensions.h" + +#include +#include + +using namespace ProjectExplorer; + +namespace { +Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg) +Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg) + +void findQmlFiles(QFutureInterface &f, const Project *project) +{ + if (!project && !f.isCanceled()) + f.reportFinished({}); + + int index = 0; + Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool { + if (f.isCanceled()) + return false; + Utils::FilePath path = node->filePath(); + bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper(); + if (isComponent && node->filePath().endsWith(".ui.qml")) + f.reportResult(path, index++); + return true; + }); + f.reportFinished(); +} +} + +namespace QmlDesigner { + +FilePathModel::FilePathModel(ProjectExplorer::Project *project, QObject *parent) + : QAbstractListModel(parent), + m_project(project) +{ + QTimer::singleShot(0, this, &FilePathModel::processProject); +} + +FilePathModel::~FilePathModel() +{ + if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && + !m_preprocessWatcher->isFinished()) { + ExportNotification::addInfo(tr("Canceling QML files preparation.")); + m_preprocessWatcher->cancel(); + m_preprocessWatcher->waitForFinished(); + qCDebug(loggerInfo) << "Canceling QML files preparation done."; + } +} + +Qt::ItemFlags FilePathModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags itemFlags = QAbstractListModel::flags(index); + if (index.isValid()) + itemFlags |= Qt::ItemIsUserCheckable; + return itemFlags; +} + +int FilePathModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return m_files.count(); + return 0; +} + +QVariant FilePathModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + switch (role) { + case Qt::DisplayRole: + return m_files[index.row()].toUserOutput(); + case Qt::CheckStateRole: + return m_skipped.count(m_files[index.row()]) ? Qt::Unchecked : Qt::Checked; + default: + break; + } + + return {}; +} + +bool FilePathModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || role != Qt::CheckStateRole) + return false; + + const Utils::FilePath path = m_files[index.row()]; + if (value == Qt::Checked) + m_skipped.erase(path); + else + m_skipped.insert(path); + + emit dataChanged(index, index); + return true; +} + +Utils::FilePaths FilePathModel::files() const +{ + Utils::FilePaths selectedPaths; + std::copy_if(m_files.begin(), m_files.end(), std::back_inserter(selectedPaths), + [this](const Utils::FilePath &path) { + return !m_skipped.count(path); + }); + return selectedPaths; +} + +void FilePathModel::processProject() +{ + if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() && + !m_preprocessWatcher->isFinished()) { + qCDebug(loggerError) << "Previous model load not finished."; + return; + } + + beginResetModel(); + m_preprocessWatcher.reset(new QFutureWatcher(this)); + connect(m_preprocessWatcher.get(), &QFutureWatcher::resultReadyAt, this, + [this](int resultIndex) { + beginInsertRows(index(0, 0) , m_files.count(), m_files.count()); + m_files.append(m_preprocessWatcher->resultAt(resultIndex)); + endInsertRows(); + }); + + connect(m_preprocessWatcher.get(), &QFutureWatcher::finished, + this, &FilePathModel::endResetModel); + + QFuture f = Utils::runAsync(&findQmlFiles, m_project); + m_preprocessWatcher->setFuture(f); +} + + +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h new file mode 100644 index 00000000000..91a800c0362 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once +#include +#include + +#include "utils/fileutils.h" + +#include +#include + +namespace ProjectExplorer { +class Project; +} + +namespace QmlDesigner { +class FilePathModel : public QAbstractListModel +{ +public: + FilePathModel(ProjectExplorer::Project *project, QObject *parent = nullptr); + ~FilePathModel() override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + Utils::FilePaths files() const; +private: + void processProject(); + + ProjectExplorer::Project *m_project = nullptr; + std::unique_ptr> m_preprocessWatcher; + std::unordered_set m_skipped; + Utils::FilePaths m_files; +}; + +} From ce942bd33c142cd52ef0b5765551f5bdf5dcd831 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 16 Jun 2020 00:05:33 +0200 Subject: [PATCH 24/46] Sqlite: Extend error handling We do only handled errors for the v1 interface. Now we handle much more. Sadly this is hard to test, so there are no tests. Change-Id: I6f55dc1bad744776bb33a6d0d6c71e52d2f097f6 Reviewed-by: Tim Jenssen --- src/libs/sqlite/sqlitebasestatement.cpp | 116 +++++++++++++++++++++--- src/libs/sqlite/sqlitebasestatement.h | 12 ++- src/libs/sqlite/sqliteexception.h | 92 ++++++++++++++++++- 3 files changed, 206 insertions(+), 14 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index ce22ece5262..dc4dd465e7c 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -251,10 +251,46 @@ sqlite3 *BaseStatement::sqliteDatabaseHandle() const void BaseStatement::checkForStepError(int resultCode) const { switch (resultCode) { - case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); - case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); - case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); - case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); + case SQLITE_BUSY: + throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to " + "acquire the database locks!"); + case SQLITE_ERROR: + throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a " + "constraint violation) has occurred!"); + case SQLITE_MISUSE: + throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); + case SQLITE_CONSTRAINT: + throwConstraintPreventsModification( + "SqliteStatement::stepStatement: contraint prevent insert or update!"); + case SQLITE_TOOBIG: + throwTooBig("SqliteStatement::stepStatement: Some is to bigger than SQLITE_MAX_LENGTH."); + case SQLITE_SCHEMA: + throwSchemaChangeError("SqliteStatement::stepStatement: Schema changed but the statement " + "cannot be recompiled."); + case SQLITE_READONLY: + throwCannotWriteToReadOnlyConnection( + "SqliteStatement::stepStatement: Cannot write to read only connection"); + case SQLITE_PROTOCOL: + throwProtocolError( + "SqliteStatement::stepStatement: Something strang with the file locking happened."); + case SQLITE_NOMEM: + throw std::bad_alloc(); + case SQLITE_NOLFS: + throwDatabaseExceedsMaximumFileSize( + "SqliteStatement::stepStatement: Database exceeds maximum file size."); + case SQLITE_MISMATCH: + throwDataTypeMismatch( + "SqliteStatement::stepStatement: Most probably you used not an integer for a rowid."); + case SQLITE_LOCKED: + throwConnectionIsLocked("SqliteStatement::stepStatement: Database connection is locked."); + case SQLITE_IOERR: + throwInputOutputError("SqliteStatement::stepStatement: An IO error happened."); + case SQLITE_INTERRUPT: + throwExecutionInterrupted("SqliteStatement::stepStatement: Execution was interrupted."); + case SQLITE_CORRUPT: + throwDatabaseIsCorrupt("SqliteStatement::stepStatement: Database is corrupt."); + case SQLITE_CANTOPEN: + throwCannotOpen("SqliteStatement::stepStatement: Cannot open database or temporary file."); } throwUnknowError("SqliteStatement::stepStatement: unknown error has happened"); @@ -263,10 +299,17 @@ void BaseStatement::checkForStepError(int resultCode) const void BaseStatement::checkForResetError(int resultCode) const { switch (resultCode) { - case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); - case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); - case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); - case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); + case SQLITE_BUSY: + throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to " + "acquire the database locks!"); + case SQLITE_ERROR: + throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a " + "constraint violation) has occurred!"); + case SQLITE_MISUSE: + throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); + case SQLITE_CONSTRAINT: + throwConstraintPreventsModification( + "SqliteStatement::stepStatement: contraint prevent insert or update!"); } throwUnknowError("SqliteStatement::reset: unknown error has happened"); @@ -278,7 +321,8 @@ void BaseStatement::checkForPrepareError(int resultCode) const case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!"); case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!"); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!"); - case SQLITE_IOERR: throwIoError("SqliteStatement::prepareStatement: IO error happened!"); + case SQLITE_IOERR: + throwInputOutputError("SqliteStatement::prepareStatement: IO error happened!"); } throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened"); @@ -346,9 +390,9 @@ void BaseStatement::throwStatementIsMisused(const char *whatHasHappened) const throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } -void BaseStatement::throwIoError(const char *whatHasHappened) const +void BaseStatement::throwInputOutputError(const char *whatHasHappened) const { - throw IoError(whatHasHappened); + throw InputOutputError(whatHasHappened); } void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const @@ -384,6 +428,56 @@ void BaseStatement::throwBingingTooBig(const char *whatHasHappened) const throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); } +void BaseStatement::throwTooBig(const char *whatHasHappened) const +{ + throw TooBig{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwSchemaChangeError(const char *whatHasHappened) const +{ + throw SchemeChangeError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const +{ + throw CannotWriteToReadOnlyConnection{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwProtocolError(const char *whatHasHappened) const +{ + throw ProtocolError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const +{ + throw DatabaseExceedsMaximumFileSize{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwDataTypeMismatch(const char *whatHasHappened) const +{ + throw DataTypeMismatch{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwConnectionIsLocked(const char *whatHasHappened) const +{ + throw ConnectionIsLocked{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())}; +} + +void BaseStatement::throwExecutionInterrupted(const char *whatHasHappened) const +{ + throw ExecutionInterrupted{whatHasHappened}; +} + +void BaseStatement::throwDatabaseIsCorrupt(const char *whatHasHappened) const +{ + throw DatabaseIsCorrupt{whatHasHappened}; +} + +void BaseStatement::throwCannotOpen(const char *whatHasHappened) const +{ + throw CannotOpen{whatHasHappened}; +} + QString BaseStatement::columnName(int column) const { return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column)); diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 2480a4674a6..bae41665d5c 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -109,7 +109,7 @@ public: [[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const; [[noreturn]] void throwStatementHasError(const char *whatHasHappened) const; [[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const; - [[noreturn]] void throwIoError(const char *whatHasHappened) const; + [[noreturn]] void throwInputOutputError(const char *whatHasHappened) const; [[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const; [[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const; [[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const; @@ -117,6 +117,16 @@ public: [[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const; [[noreturn]] void throwUnknowError(const char *whatHasHappened) const; [[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const; + [[noreturn]] void throwTooBig(const char *whatHasHappened) const; + [[noreturn]] void throwSchemaChangeError(const char *whatHasHappened) const; + [[noreturn]] void throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const; + [[noreturn]] void throwProtocolError(const char *whatHasHappened) const; + [[noreturn]] void throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const; + [[noreturn]] void throwDataTypeMismatch(const char *whatHasHappened) const; + [[noreturn]] void throwConnectionIsLocked(const char *whatHasHappened) const; + [[noreturn]] void throwExecutionInterrupted(const char *whatHasHappened) const; + [[noreturn]] void throwDatabaseIsCorrupt(const char *whatHasHappened) const; + [[noreturn]] void throwCannotOpen(const char *whatHasHappened) const; QString columnName(int column) const; diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h index bd4245934c8..a46d30d851d 100644 --- a/src/libs/sqlite/sqliteexception.h +++ b/src/libs/sqlite/sqliteexception.h @@ -91,10 +91,10 @@ public: } }; -class IoError : public Exception +class InputOutputError : public Exception { public: - IoError(const char *whatErrorHasHappen) + InputOutputError(const char *whatErrorHasHappen) : Exception(whatErrorHasHappen) { } @@ -267,6 +267,14 @@ public: } }; +class TooBig : public Exception +{ +public: + TooBig(const char *whatErrorHasHappen, Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + class CannotConvert : public Exception { public: @@ -299,4 +307,84 @@ public: {} }; +class SchemeChangeError : public Exception +{ +public: + SchemeChangeError(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class CannotWriteToReadOnlyConnection : public Exception +{ +public: + CannotWriteToReadOnlyConnection(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class ProtocolError : public Exception +{ +public: + ProtocolError(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class DatabaseExceedsMaximumFileSize : public Exception +{ +public: + DatabaseExceedsMaximumFileSize(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class DataTypeMismatch : public Exception +{ +public: + DataTypeMismatch(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class ConnectionIsLocked : public Exception +{ +public: + ConnectionIsLocked(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class ExecutionInterrupted : public Exception +{ +public: + ExecutionInterrupted(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class DatabaseIsCorrupt : public Exception +{ +public: + DatabaseIsCorrupt(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; + +class CannotOpen : public Exception +{ +public: + CannotOpen(const char *whatErrorHasHappen, + Utils::SmallString &&errorMessage = Utils::SmallString()) + : Exception(whatErrorHasHappen, std::move(errorMessage)) + {} +}; } // namespace Sqlite From f77d1df624ac968c78187e4f4e9853a9c84c6b4b Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 18 Jun 2020 19:32:19 +0200 Subject: [PATCH 25/46] AssetExport: Delay the load signal from view Aparantly the QML file is not completely loaded when model is attached Change-Id: If8c342c057a1cd33b6f7510bbf9f56a1f3369961 Reviewed-by: Tim Jenssen --- .../qmldesigner/assetexporterplugin/assetexporterview.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp index 193ebce62aa..1e47632c8ef 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp @@ -117,8 +117,11 @@ void AssetExporterView::setState(AssetExporterView::LoadState state) qCDebug(loggerInfo) << "Loading state changed" << m_state; if (inErrorState() || m_state == LoadState::Loaded) { m_timer.stop(); + // TODO: Send the loaded signal with a delay. The assumption that model attached and a + // valid root object is enough to declare a QML file is ready is incorrect. A ideal + // solution would be that the puppet notifies file ready signal. if (m_state == LoadState::Loaded) - emit loadingFinished(); + QTimer::singleShot(2000, this, &AssetExporterView::loadingFinished); else emit loadingError(m_state); } From c12e3d58eec83adb7f4951e3fddd2a170f67e1e3 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 12 Jun 2020 10:48:46 +0200 Subject: [PATCH 26/46] AssetExport: Add text node parsing Change-Id: I86e40aef49d27515d798f8511a6ed15786b2358e Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetexporterplugin.cpp | 2 + .../assetexporterplugin.pri | 6 +- .../assetexporterplugin.qbs | 4 +- .../assetexportpluginconstants.h | 42 +++++++++ .../parsers/modelitemnodeparser.cpp | 21 ++--- .../parsers/modelnodeparser.cpp | 6 ++ .../parsers/modelnodeparser.h | 11 ++- .../parsers/textnodeparser.cpp | 86 +++++++++++++++++++ .../parsers/textnodeparser.h | 41 +++++++++ 10 files changed, 203 insertions(+), 17 deletions(-) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0e91850e82d..866ca57cbc5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -54,6 +54,7 @@ add_qtc_plugin(assetexporterplugin assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp + assetexporterplugin/parsers/textnodeparser.h assetexporterplugin/parsers/textnodeparser.cpp assetexporterplugin/assetexporterplugin.qrc PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} SKIP_DEBUG_CMAKE_FILE_CHECK diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index 1758201bf7a..fcd492cb0e3 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -33,6 +33,7 @@ #include "componentexporter.h" #include "parsers/modelitemnodeparser.h" +#include "parsers/textnodeparser.h" #include "coreplugin/actionmanager/actionmanager.h" #include "coreplugin/actionmanager/actioncontainer.h" @@ -68,6 +69,7 @@ AssetExporterPlugin::AssetExporterPlugin() : // Add parsers templates for factory instantiation. ComponentExporter::addNodeParser(); + ComponentExporter::addNodeParser(); // Instantiate actions created by the plugin. addActions(); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri index d4ff10ec165..b0e4f6392a5 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -16,7 +16,8 @@ HEADERS += \ exportnotification.h \ filepathmodel.h \ parsers/modelitemnodeparser.h \ - parsers/modelnodeparser.h + parsers/modelnodeparser.h \ + parsers/textnodeparser.h SOURCES += \ assetexportdialog.cpp \ @@ -27,7 +28,8 @@ SOURCES += \ exportnotification.cpp \ filepathmodel.cpp \ parsers/modelitemnodeparser.cpp \ - parsers/modelnodeparser.cpp + parsers/modelnodeparser.cpp \ + parsers/textnodeparser.cpp FORMS += \ assetexportdialog.ui diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs index 56c7659e102..e8ce2537361 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -50,6 +50,8 @@ QtcProduct { "parsers/modelitemnodeparser.cpp", "parsers/modelitemnodeparser.h", "parsers/modelnodeparser.cpp", - "parsers/modelnodeparser.h" + "parsers/modelnodeparser.h", + "parsers/textnodeparser.cpp", + "parsers/textnodeparser.h" ] } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 79145bb1456..169d99ebfbe 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -31,5 +31,47 @@ const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml"; const char TASK_CATEGORY_ASSET_EXPORT[] = "AssetExporter.Export"; +//*************************************************************************** +// Metadata tags +//*************************************************************************** +// Plugin info tags +const char PluginInfoTag[] = "pluginInfo"; +const char MetadataVersionTag[] = "metadataVersion"; + +const char DocumentInfoTag[] = "documentInfo"; +const char DocumentNameTag[] = "name"; + +// Layer data tags +const char ArtboardListTag[] = "artboards"; + +const char XPosTag[] = "x"; +const char YPosTag[] = "y"; +const char WidthTag[] = "width"; +const char HeightTag[] = "height"; + + +const char QmlIdTag[] = "qmlId"; +const char ExportTypeTag[] = "exportType"; +const char QmlPropertiesTag[] = "qmlProperties"; +const char ImportsTag[] = "extraImports"; +const char UuidTag[] = "uuid"; +const char ClipTag[] = "clip"; +const char AssetDataTag[] = "assetData"; +const char AssetPath[] = "assetPath"; +const char AssetBoundsTag[] = "assetBounds"; +const char OpacityTag[] = "opacity"; + +const char TextDetailsTag[] = "textDetails"; +const char FontFamilyTag[] = "fontFamily"; +const char FontSizeTag[] = "fontSize"; +const char FontStyleTag[] = "fontStyle"; +const char LetterSpacingTag[] = "kerning"; +const char TextColorTag[] = "textColor"; +const char TextContentTag[] = "contents"; +const char IsMultilineTag[] = "multiline"; +const char LineHeightTag[] = "lineHeight"; +const char HAlignTag[] = "horizontalAlignment"; +const char VAlignTag[] = "verticalAlignment"; + } } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp index ee89c824ac5..08366c4119b 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp @@ -26,13 +26,10 @@ #include "modelitemnodeparser.h" #include "assetexportpluginconstants.h" -#include "modelnode.h" #include "qmlitemnode.h" -#include "variantproperty.h" - namespace QmlDesigner { - +using namespace Constants; ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node) : ModelNodeParser(lineage, node) { @@ -41,25 +38,25 @@ ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, const ModelNode &n bool QmlDesigner::ItemNodeParser::isExportable() const { - return m_lineage.contains("QtQuick.Item"); + return lineage().contains("QtQuick.Item"); } QJsonObject QmlDesigner::ItemNodeParser::json() const { - // TODO parse other relevant properties i.e. dimensions etc + const QmlObjectNode &qmlObjectNode = objectNode(); QJsonObject jsonObject; - jsonObject.insert("qmlid", m_node.id()); - QmlItemNode itemNode(m_node); + jsonObject.insert(QmlIdTag, qmlObjectNode.id()); + QmlItemNode itemNode = qmlObjectNode.toQmlItemNode(); // Position relative to parent QPointF pos = itemNode.instancePosition(); - jsonObject.insert("x", pos.x()); - jsonObject.insert("y", pos.y()); + jsonObject.insert(XPosTag, pos.x()); + jsonObject.insert(YPosTag, pos.y()); // size QSizeF size = itemNode.instanceSize(); - jsonObject.insert("width", size.width()); - jsonObject.insert("height", size.height()); + jsonObject.insert(WidthTag, size.width()); + jsonObject.insert(HeightTag, size.height()); return jsonObject; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp index 29d6f305e60..31787b83cc3 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp @@ -27,9 +27,15 @@ namespace QmlDesigner { ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) : m_node(node), + m_objectNode(node), m_lineage(lineage) { } +QVariant ModelNodeParser::propertyValue(const PropertyName &name) const +{ + return m_objectNode.instanceValue(name); +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h index 744abfd3fde..6b0807d5371 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h @@ -24,6 +24,8 @@ ****************************************************************************/ #pragma once +#include "qmlobjectnode.h" + #include #include @@ -41,8 +43,13 @@ public: virtual bool isExportable() const = 0; virtual QJsonObject json() const = 0; -protected: + const QByteArrayList& lineage() const { return m_lineage; } + const QmlObjectNode& objectNode() const { return m_objectNode; } + QVariant propertyValue(const PropertyName &name) const; + +private: const ModelNode &m_node; - const QByteArrayList m_lineage; + QmlObjectNode m_objectNode; + QByteArrayList m_lineage; }; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp new file mode 100644 index 00000000000..1f5a9e70280 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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 "textnodeparser.h" +#include "assetexportpluginconstants.h" + +#include +#include + +namespace { +const QHash AlignMapping{ + {"AlignRight", "RIGHT"}, + {"AlignHCenter", "CENTER"}, + {"AlignJustify", "JUSTIFIED"}, + {"AlignLeft", "LEFT"}, + {"AlignTop", "TOP"}, + {"AlignVCenter", "CENTER"}, + {"AlignBottom", "BOTTOM"} +}; + +QString toJsonAlignEnum(QString value) { + if (value.isEmpty() || !AlignMapping.contains(value)) + return ""; + return AlignMapping[value]; +} +} + + +namespace QmlDesigner { +using namespace Constants; +TextNodeParser::TextNodeParser(const QByteArrayList &lineage, const ModelNode &node) : + ItemNodeParser(lineage, node) +{ + +} + +bool TextNodeParser::isExportable() const +{ + return lineage().contains("QtQuick.Text"); +} + +QJsonObject TextNodeParser::json() const +{ + QJsonObject jsonObject = ItemNodeParser::json(); + + QJsonObject textDetails; + textDetails.insert(TextContentTag, propertyValue("text").toString()); + textDetails.insert(FontFamilyTag, propertyValue("font.family").toString()); + textDetails.insert(FontStyleTag, propertyValue("font.styleName").toString()); + textDetails.insert(FontSizeTag, propertyValue("font.pixelSize").toInt()); + textDetails.insert(LetterSpacingTag, propertyValue("font.letterSpacing").toFloat()); + + QColor fontColor(propertyValue("font.color").toString()); + textDetails.insert(TextColorTag, fontColor.name(QColor::HexArgb)); + + textDetails.insert(HAlignTag, toJsonAlignEnum(propertyValue("horizontalAlignment").toString())); + textDetails.insert(VAlignTag, toJsonAlignEnum(propertyValue("verticalAlignment").toString())); + + textDetails.insert(IsMultilineTag, propertyValue("wrapMode").toString().compare("NoWrap") != 0); + + jsonObject.insert(TextDetailsTag, textDetails); + return jsonObject; +} +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h new file mode 100644 index 00000000000..29c5fbf3c56 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** 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 "modelitemnodeparser.h" + +namespace QmlDesigner { +class TextNodeParser : public ItemNodeParser +{ +public: + TextNodeParser(const QByteArrayList &lineage, const ModelNode &node); + ~TextNodeParser() override = default; + + bool isExportable() const override; + int priority() const override { return 200; } + QJsonObject json() const override; +}; + +} From dff01adf94f9d1560a038af7b3eb43d04e5c12ca Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 18 Jun 2020 21:21:25 +0200 Subject: [PATCH 27/46] AssetExport: Let user open export location Change-Id: Ie508552446c9eecd6c2ee47eeb43c25230579040 Reviewed-by: Leena Miettinen Reviewed-by: Tim Jenssen --- .../assetexporterplugin/assetexportdialog.cpp | 15 +++++++++++---- .../assetexporterplugin/assetexportdialog.ui | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index ee5c9af64c1..dee7dc5462d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -28,6 +28,8 @@ #include "assetexportpluginconstants.h" #include "filepathmodel.h" +#include "coreplugin/fileutils.h" +#include "coreplugin/icore.h" #include "projectexplorer/task.h" #include "projectexplorer/taskhub.h" #include "utils/fileutils.h" @@ -75,6 +77,14 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, m_outputFormatter(new Utils::OutputFormatter()) { m_ui->setupUi(this); + + m_ui->exportPath->setFileName(exportPath); + m_ui->exportPath->setPromptDialogTitle(tr("Choose Export Path")); + m_ui->exportPath->lineEdit()->setReadOnly(true); + m_ui->exportPath->addButton(tr("Open"), this, [this]() { + Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_ui->exportPath->path()); + }); + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_ui->stackedWidget->addWidget(m_filesView); @@ -103,9 +113,6 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, }); m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false); - m_ui->exportPathEdit->setFileName(exportPath); - m_ui->exportPathEdit->setPromptDialogTitle(tr("Choose Export Path")); - connect(&m_assetExporter, &AssetExporter::stateChanged, this, &AssetExportDialog::onExportStateChanged); connect(&m_assetExporter, &AssetExporter::exportProgressChanged, @@ -129,7 +136,7 @@ void AssetExportDialog::onExport() TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT); m_exportLogs->clear(); - m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPathEdit->fileName()); + m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName()); } void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui index fb45d9c19d2..38c2152098c 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -15,7 +15,7 @@ - + From 22abe8ef1c10cf0058f4ee21417982ea598a8775 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 18 Jun 2020 21:53:49 +0200 Subject: [PATCH 28/46] AssetExport: Add export notification when export finishes Change-Id: I6c9227fcc25c43415dec1afdaf95da5a1430a700 Reviewed-by: Leena Miettinen Reviewed-by: Tim Jenssen --- .../qmldesigner/assetexporterplugin/assetexportdialog.cpp | 1 - src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index dee7dc5462d..b41ce82b2a0 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -145,7 +145,6 @@ void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newStat case AssetExporter::ParsingState::ExportingDone: m_exportBtn->setVisible(false); m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true); - QMessageBox::information(this, tr("QML Export"), tr("Done")); break; default: break; diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 609da3f80e2..27c83ff5d68 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -164,6 +164,7 @@ void AssetExporter::writeMetadata() const } } notifyProgress(1.0); + ExportNotification::addInfo(tr("Export finished.")); m_currentState.change(ParsingState::ExportingDone); } From aa8bf1f448dcd5b63baa90050001c3b8b32882fb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 24 Jun 2020 16:54:58 +0200 Subject: [PATCH 29/46] QmlDesigner: Add properties for sorting Change-Id: I887e3b06ca3accc243283e1ac0895ab81bbdf513 Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/modeltotextmerger.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp index 18bea1990cb..b35a54a3fee 100644 --- a/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modeltotextmerger.cpp @@ -388,6 +388,8 @@ PropertyNameList ModelToTextMerger::propertyOrder() PropertyName("wrapMode"), PropertyName(), PropertyName("states"), + PropertyName("to"), + PropertyName("from"), PropertyName("transitions") }; From 3cb12a51ceb658a729c86192101a484e1e9c9158 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 24 Jun 2020 16:54:18 +0200 Subject: [PATCH 30/46] QmlDesigner: Increase default for canvas size Change-Id: Id37aaa5bca253751781c02f9dc44343db137069a Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designersettings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index 91091c53d66..c0e8b3bdf1d 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -50,8 +50,8 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::ITEMSPACING, 6); restoreValue(settings, DesignerSettingsKey::CONTAINERPADDING, 8); - restoreValue(settings, DesignerSettingsKey::CANVASWIDTH, 40000); - restoreValue(settings, DesignerSettingsKey::CANVASHEIGHT, 40000); + restoreValue(settings, DesignerSettingsKey::CANVASWIDTH, 100000); + restoreValue(settings, DesignerSettingsKey::CANVASHEIGHT, 100000); restoreValue(settings, DesignerSettingsKey::ROOT_ELEMENT_INIT_WIDTH, 640); restoreValue(settings, DesignerSettingsKey::ROOT_ELEMENT_INIT_HEIGHT, 480); restoreValue(settings, DesignerSettingsKey::WARNING_FOR_FEATURES_IN_DESIGNER, true); From f26c51d5626d0e2dbb1e3743c8993a744fdea832 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 24 Jun 2020 16:55:14 +0200 Subject: [PATCH 31/46] QmlDesigner: Add missing static Change-Id: Ic71aeeb8f6cf65944672a0f1d09f2d70e824d760 Reviewed-by: Thomas Hartmann Reviewed-by: Knud Dollereder --- .../components/timelineeditor/timelinetoolbar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp index dec61de19fb..39874f51b06 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetoolbar.cpp @@ -56,19 +56,19 @@ namespace QmlDesigner { -bool isSpacer(QObject *object) +static bool isSpacer(QObject *object) { return object->property("spacer_widget").toBool(); } -QWidget *createSpacer() +static QWidget *createSpacer() { QWidget *spacer = new QWidget(); spacer->setProperty("spacer_widget", true); return spacer; } -int controlWidth(QToolBar *bar, QObject *control) +static int controlWidth(QToolBar *bar, QObject *control) { QWidget *widget = nullptr; @@ -84,7 +84,7 @@ int controlWidth(QToolBar *bar, QObject *control) return 0; } -QAction *createAction(const Core::Id &id, +static QAction *createAction(const Core::Id &id, const QIcon &icon, const QString &name, const QKeySequence &shortcut) From 3c5f3547fb65c26082bfd5322317d6798e394ce0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 16 Jun 2020 17:05:35 +0200 Subject: [PATCH 32/46] QmlDesigner: Properly resolve "dot properties" for dynamic objects property NumberAnimation numberAnimation: NumberAnimation { } In this case this will expose the "dot properties" of numberAnimation. Change-Id: I40aafcc4f762ab6297c0829674ac62d92cbd713d Reviewed-by: Tim Jenssen --- .../designercore/metainfo/nodemetainfo.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 4abf7d28023..01817a97e63 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -88,11 +88,17 @@ static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPt if (ref->ast()->propertyToken.isValid()) { type = ref->ast()->memberType->name.toUtf8(); - if (type == "alias") { - const Value *value = context->lookupReference(ref); + const Value *value = context->lookupReference(ref); - if (!value) - return type; + if (!value) + return type; + + if (const CppComponentValue * componentObjectValue = value->asCppComponentValue()) { + type = componentObjectValue->className().toUtf8(); + dotProperties = getObjectTypes(componentObjectValue, context); + } + + if (type == "alias") { if (const ASTObjectValue * astObjectValue = value->asAstObjectValue()) { if (astObjectValue->typeName()) { From 6dda9d360d4fc9912c13dca35b772053c89c6619 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 25 Jun 2020 13:19:54 +0200 Subject: [PATCH 33/46] Add missing include Change-Id: I870cf23c4ae994b67a3e938dae767b7f553deb56 Reviewed-by: Vikas Pachdha --- .../qmldesigner/assetexporterplugin/assetexportdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index b41ce82b2a0..0b0c3c8af84 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -43,6 +43,7 @@ #include #include +#include namespace { static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str, From 72302cac4902178e38489c2cc60f4897e9dbc5f1 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 24 Jun 2020 16:54:38 +0200 Subject: [PATCH 34/46] QmlDesigner: Remove old QtQuick 1 code Change-Id: I523419689ab7667900de6ca72aa736e14064ab34 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/model/qmlstate.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp index e23a87b3ef3..89bbec351eb 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp @@ -283,10 +283,7 @@ ModelNode QmlModelState::createQmlState(AbstractView *view, const PropertyListTy { QTC_CHECK(view->majorQtQuickVersion() < 3); - if (view->majorQtQuickVersion() > 1) - return view->createModelNode("QtQuick.State", 2, 0, propertyList); - else - return view->createModelNode("QtQuick.State", 1, 0, propertyList); + return view->createModelNode("QtQuick.State", 2, 0, propertyList); } void QmlModelState::setAsDefault() From 003282c6f5ed89de4bc5a2ddeffdea59286b455f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 16 Jun 2020 17:05:56 +0200 Subject: [PATCH 35/46] QmlDesigner: Fix crash on puppet Change-Id: I233d3dab5a509bfd7c9a30c7dcc15dcf8751f456 Reviewed-by: Tim Jenssen --- .../qml2puppet/instances/objectnodeinstance.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 840b71c6711..5991e2b28e9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -779,7 +779,9 @@ QObject *ObjectNodeInstance::createComponent(const QUrl &componentUrl, QQmlConte return QmlPrivateGate::createComponent(componentUrl, context); } -QObject *ObjectNodeInstance::createCustomParserObject(const QString &nodeSource, const QByteArray &importCode, QQmlContext *context) +QObject *ObjectNodeInstance::createCustomParserObject(const QString &nodeSource, + const QByteArray &importCode, + QQmlContext *context) { QmlPrivateGate::ComponentCompleteDisabler disableComponentComplete; Q_UNUSED(disableComponentComplete) @@ -790,9 +792,11 @@ QObject *ObjectNodeInstance::createCustomParserObject(const QString &nodeSource, data.prepend(importCode); component.setData(data, context->baseUrl().resolved(QUrl("createCustomParserObject.qml"))); QObject *object = component.beginCreate(context); - QmlPrivateGate::tweakObjects(object); - component.completeCreate(); - QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); + if (object) { + QmlPrivateGate::tweakObjects(object); + component.completeCreate(); + QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); + } if (component.isError()) { qWarning() << "Error in:" << Q_FUNC_INFO << component.url().toString(); From a8ac06a852d105f0fff29379fbfb201c156df396 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 25 Jun 2020 12:10:55 +0200 Subject: [PATCH 36/46] qmldesigner: reduce noise removes unnecessary: "PropertyEditor: invalid node for setup" while reseting the view. Change-Id: Ib4583e97ed5a52ff87116e4231f4bf6b4f4cc7e5 Reviewed-by: Thomas Hartmann --- .../components/propertyeditor/propertyeditorview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 5307bf522b3..61c40b95606 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -470,8 +470,8 @@ void PropertyEditorView::setupQmlBackend() if (m_selectedNode.isValid()) { qmlObjectNode = QmlObjectNode(m_selectedNode); Q_ASSERT(qmlObjectNode.isValid()); + currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); } - currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false)); if (specificQmlData.isEmpty()) currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); From 02d982dc8d2250fc992a16eba24e6316018c8428 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 25 Jun 2020 12:13:27 +0200 Subject: [PATCH 37/46] qmldesigner: remove unreferenced variable Change-Id: Id76291e2621744a91ae4eb78d3941086ee7facd7 Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/propertyeditor/propertyeditorview.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 61c40b95606..742e8b344a2 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -212,7 +212,6 @@ void PropertyEditorView::changeValue(const QString &name) } } - bool forceReset = false; if (name == "state" && castedValue.toString() == "base state") castedValue = ""; From 4e9b6d68c180f3df76fb30a0d9b6fb5bebabc7f5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 24 Jun 2020 16:03:29 +0200 Subject: [PATCH 38/46] QmlDesigner: Break dependencies in the timeline graphics view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to reuse big parts of the timeline infrastructure. To allow this I introduced AbstractScrollGraphicsScene and other view can derive from it to implement similar behaivour. Change-Id: I4a4d2d5711d4a4fbb4cf7af29f689459ccf89f00 Reviewed-by: Tim Jenssen Reviewed-by: Henning Gründl --- .../timelineeditor/timelineabstracttool.cpp | 6 +- .../timelineeditor/timelineabstracttool.h | 10 +- .../timelineeditor/timelinegraphicsscene.cpp | 50 ++++++--- .../timelineeditor/timelinegraphicsscene.h | 100 ++++++++++++------ .../timelineeditor/timelineitem.cpp | 42 ++++---- .../components/timelineeditor/timelineitem.h | 4 + .../timelinemovableabstractitem.cpp | 14 +-- .../timelinemovableabstractitem.h | 4 +- .../timelineeditor/timelinemovetool.cpp | 4 +- .../timelineeditor/timelinemovetool.h | 2 +- .../timelineeditor/timelinepropertyitem.cpp | 21 ++-- .../timelineeditor/timelinepropertyitem.h | 1 + .../timelineeditor/timelinesectionitem.cpp | 50 +++++---- .../timelineeditor/timelinesectionitem.h | 1 + .../timelineeditor/timelineselectiontool.cpp | 2 +- .../timelineeditor/timelineselectiontool.h | 2 +- .../timelineeditor/timelinetooldelegate.cpp | 2 +- .../timelineeditor/timelinetooldelegate.h | 4 +- 18 files changed, 194 insertions(+), 125 deletions(-) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.cpp index 485bb8dbb01..f72b7e570cc 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.cpp @@ -31,12 +31,12 @@ namespace QmlDesigner { -TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene) +TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene) : m_scene(scene) , m_delegate(nullptr) {} -TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene, +TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate) : m_scene(scene) , m_delegate(delegate) @@ -44,7 +44,7 @@ TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene, TimelineAbstractTool::~TimelineAbstractTool() = default; -TimelineGraphicsScene *TimelineAbstractTool::scene() const +AbstractScrollGraphicsScene *TimelineAbstractTool::scene() const { return m_scene; } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.h b/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.h index 0411a8d1664..7d62ceaffc3 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineabstracttool.h @@ -36,14 +36,14 @@ namespace QmlDesigner { enum class ToolType { Move, Select }; class TimelineMovableAbstractItem; -class TimelineGraphicsScene; +class AbstractScrollGraphicsScene; class TimelineToolDelegate; class TimelineAbstractTool { public: - explicit TimelineAbstractTool(TimelineGraphicsScene *scene); - explicit TimelineAbstractTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); + explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene); + explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate); virtual ~TimelineAbstractTool(); virtual void mousePressEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) = 0; @@ -56,7 +56,7 @@ public: virtual void keyPressEvent(QKeyEvent *keyEvent) = 0; virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0; - TimelineGraphicsScene *scene() const; + AbstractScrollGraphicsScene *scene() const; TimelineToolDelegate *delegate() const; @@ -65,7 +65,7 @@ public: TimelineMovableAbstractItem *currentItem() const; private: - TimelineGraphicsScene *m_scene; + AbstractScrollGraphicsScene *m_scene; TimelineToolDelegate *m_delegate; }; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp index 6c252562493..8caccbf866e 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp @@ -91,7 +91,7 @@ QList allTimelineFrames(const QmlTimeline &timeline) } TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *parent) - : QGraphicsScene(parent) + : AbstractScrollGraphicsScene(parent) , m_parent(parent) , m_layout(new TimelineGraphicsLayout(this)) , m_currentFrameIndicator(new TimelineFrameHandle) @@ -378,17 +378,17 @@ void TimelineGraphicsScene::commitCurrentFrame(qreal frame) } } -QList TimelineGraphicsScene::selectedKeyframes() const +QList AbstractScrollGraphicsScene::selectedKeyframes() const { return m_selectedKeyframes; } -bool TimelineGraphicsScene::hasSelection() const +bool AbstractScrollGraphicsScene::hasSelection() const { return !m_selectedKeyframes.empty(); } -bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const +bool AbstractScrollGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const { if (m_selectedKeyframes.empty()) return false; @@ -396,12 +396,12 @@ bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const return m_selectedKeyframes.back() == keyframe; } -bool TimelineGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const +bool AbstractScrollGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const { return m_selectedKeyframes.contains(keyframe); } -bool TimelineGraphicsScene::multipleKeyframesSelected() const +bool AbstractScrollGraphicsScene::multipleKeyframesSelected() const { return m_selectedKeyframes.count() > 1; } @@ -456,19 +456,19 @@ void TimelineGraphicsScene::invalidateRecordButtonsStatus() TimelinePropertyItem::updateRecordButtonStatus(item); } -int TimelineGraphicsScene::scrollOffset() const +int AbstractScrollGraphicsScene::scrollOffset() const { return m_scrollOffset; } -void TimelineGraphicsScene::setScrollOffset(int offset) +void AbstractScrollGraphicsScene::setScrollOffset(int offset) { m_scrollOffset = offset; emitScrollOffsetChanged(); update(); } -QGraphicsView *TimelineGraphicsScene::graphicsView() const +QGraphicsView *AbstractScrollGraphicsScene::graphicsView() const { for (auto *v : views()) if (v->objectName() == "SceneView") @@ -477,7 +477,7 @@ QGraphicsView *TimelineGraphicsScene::graphicsView() const return nullptr; } -QGraphicsView *TimelineGraphicsScene::rulerView() const +QGraphicsView *AbstractScrollGraphicsScene::rulerView() const { for (auto *v : views()) if (v->objectName() == "RulerView") @@ -491,7 +491,7 @@ QmlTimeline TimelineGraphicsScene::currentTimeline() const return QmlTimeline(timelineModelNode()); } -QRectF TimelineGraphicsScene::selectionBounds() const +QRectF AbstractScrollGraphicsScene::selectionBounds() const { QRectF bbox; @@ -501,7 +501,7 @@ QRectF TimelineGraphicsScene::selectionBounds() const return bbox; } -void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode, +void AbstractScrollGraphicsScene::selectKeyframes(const SelectionMode &mode, const QList &items) { if (mode == SelectionMode::Remove || mode == SelectionMode::Toggle) { @@ -536,13 +536,14 @@ void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode, emit selectionChanged(); } -void TimelineGraphicsScene::clearSelection() +void AbstractScrollGraphicsScene::clearSelection() { for (auto *keyframe : m_selectedKeyframes) if (keyframe) keyframe->setHighlighted(false); m_selectedKeyframes.clear(); + emit selectionChanged(); } QList TimelineGraphicsScene::itemsAt(const QPointF &pos) @@ -681,7 +682,7 @@ ModelNode TimelineGraphicsScene::timelineModelNode() const void TimelineGraphicsScene::handleKeyframeDeletion() { QList nodesToBeDeleted; - for (auto keyframe : m_selectedKeyframes) { + for (auto keyframe : selectedKeyframes()) { nodesToBeDeleted.append(keyframe->frameNode()); } deleteKeyframes(nodesToBeDeleted); @@ -710,7 +711,7 @@ void TimelineGraphicsScene::pasteKeyframesToTarget(const ModelNode &targetNode) void TimelineGraphicsScene::copySelectedKeyframes() { TimelineActions::copyKeyframes( - Utils::transform(m_selectedKeyframes, &TimelineKeyframeItem::frameNode)); + Utils::transform(selectedKeyframes(), &TimelineKeyframeItem::frameNode)); } void TimelineGraphicsScene::pasteSelectedKeyframes() @@ -756,7 +757,20 @@ void TimelineGraphicsScene::activateLayout() m_layout->activate(); } -void TimelineGraphicsScene::emitScrollOffsetChanged() +AbstractView *TimelineGraphicsScene::abstractView() const +{ + return timelineView(); +} + +int AbstractScrollGraphicsScene::getScrollOffset(QGraphicsScene *scene) +{ + auto scrollScene = qobject_cast(scene); + if (scrollScene) + return scrollScene->scrollOffset(); + return 0; +} + +void AbstractScrollGraphicsScene::emitScrollOffsetChanged() { for (QGraphicsItem *item : items()) TimelineMovableAbstractItem::emitScrollOffsetChanged(item); @@ -783,4 +797,8 @@ bool TimelineGraphicsScene::event(QEvent *event) } } +QmlDesigner::AbstractScrollGraphicsScene::AbstractScrollGraphicsScene(QWidget *parent) + : QGraphicsScene(parent) +{} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h index 4e33ce362ee..7413cb1dbb2 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h @@ -36,6 +36,7 @@ QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout) QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QWidget) namespace QmlDesigner { @@ -51,15 +52,66 @@ class TimelinePlaceholder; class TimelineGraphicsLayout; class TimelineToolBar; -class TimelineGraphicsScene : public QGraphicsScene +class AbstractScrollGraphicsScene : public QGraphicsScene { Q_OBJECT -signals: - void selectionChanged(); +public: + AbstractScrollGraphicsScene(QWidget *parent); + ; + int scrollOffset() const; + void setScrollOffset(int offset); + static int getScrollOffset(QGraphicsScene *scene); + + QRectF selectionBounds() const; + + void selectKeyframes(const SelectionMode &mode, const QList &items); + virtual void clearSelection(); + QList selectedKeyframes() const; + bool hasSelection() const; + bool isCurrent(TimelineKeyframeItem *keyframe) const; + bool isKeyframeSelected(TimelineKeyframeItem *keyframe) const; + bool multipleKeyframesSelected() const; + + virtual qreal rulerScaling() const = 0; + virtual int rulerWidth() const = 0; + virtual qreal rulerDuration() const = 0; + + virtual AbstractView *abstractView() const = 0; + + virtual void setCurrentFrame(int) {} + + virtual qreal startFrame() const = 0; + virtual qreal endFrame() const = 0; + + virtual void invalidateScrollbar() = 0; + + virtual qreal snap(qreal frame, bool snapToPlayhead = true) + { + Q_UNUSED(snapToPlayhead); + return frame; + } + + QGraphicsView *graphicsView() const; + QGraphicsView *rulerView() const; + +signals: + void statusBarMessageChanged(const QString &message); + void selectionChanged(); void scroll(const TimelineUtils::Side &side); +private: + void emitScrollOffsetChanged(); + + int m_scrollOffset = 0; + QList m_selectedKeyframes; +}; + +class TimelineGraphicsScene : public AbstractScrollGraphicsScene +{ + Q_OBJECT + public: explicit TimelineGraphicsScene(TimelineWidget *parent); @@ -74,7 +126,7 @@ public: void invalidateLayout(); qreal setCurrenFrame(const QmlTimeline &timeline, qreal frame); - void setCurrentFrame(int frame); + void setCurrentFrame(int frame) override; void setStartFrame(int frame); void setEndFrame(int frame); @@ -82,11 +134,12 @@ public: TimelineWidget *timelineWidget() const; TimelineToolBar *toolBar() const; - qreal rulerScaling() const; - int rulerWidth() const; - qreal rulerDuration() const; - qreal startFrame() const; - qreal endFrame() const; + qreal rulerScaling() const override; + int rulerWidth() const override; + qreal rulerDuration() const override; + + qreal startFrame() const override; + qreal endFrame() const override; void updateKeyframePositionsCache(); @@ -97,39 +150,22 @@ public: QVector keyframePositions() const; QVector keyframePositions(const QmlTimelineKeyframeGroup &frames) const; - qreal snap(qreal frame, bool snapToPlayhead = true); + qreal snap(qreal frame, bool snapToPlayhead = true) override; void setRulerScaling(int scaling); void commitCurrentFrame(qreal frame); - QList selectedKeyframes() const; - - bool hasSelection() const; - bool isCurrent(TimelineKeyframeItem *keyframe) const; - bool isKeyframeSelected(TimelineKeyframeItem *keyframe) const; - bool multipleKeyframesSelected() const; - void invalidateSectionForTarget(const ModelNode &modelNode); void invalidateKeyframesForTarget(const ModelNode &modelNode); void invalidateScene(); - void invalidateScrollbar(); + void invalidateScrollbar() override; void invalidateCurrentValues(); void invalidateRecordButtonsStatus(); - int scrollOffset() const; - void setScrollOffset(int offset); - QGraphicsView *graphicsView() const; - QGraphicsView *rulerView() const; - QmlTimeline currentTimeline() const; - QRectF selectionBounds() const; - - void selectKeyframes(const SelectionMode &mode, const QList &items); - void clearSelection(); - void handleKeyframeDeletion(); void deleteAllKeyframesForTarget(const ModelNode &targetNode); void insertAllKeyframesForTarget(const ModelNode &targetNode); @@ -143,8 +179,7 @@ public: void activateLayout(); -signals: - void statusBarMessageChanged(const QString &message); + AbstractView *abstractView() const override; protected: bool event(QEvent *event) override; @@ -163,7 +198,6 @@ private: void invalidateSections(); ModelNode timelineModelNode() const; - void emitScrollOffsetChanged(); void emitStatusBarPlayheadFrameChanged(int frame); QList itemsAt(const QPointF &pos); @@ -178,12 +212,8 @@ private: TimelineToolDelegate m_tools; - QList m_selectedKeyframes; - // sorted, unique cache of keyframes positions, used for snapping QVector m_keyframePositionsCache; - - int m_scrollOffset = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineitem.cpp index 3b7f8a2fe0f..6d7fb552621 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineitem.cpp @@ -52,8 +52,7 @@ TimelineItem::TimelineItem(TimelineItem *parent) TimelineGraphicsScene *TimelineItem::timelineScene() const { - return static_cast(scene()); - ; + return qobject_cast(scene()); } TimelineFrameHandle::TimelineFrameHandle(TimelineItem *parent) @@ -93,7 +92,7 @@ void TimelineFrameHandle::setPosition(qreal frame) void TimelineFrameHandle::setPositionInteractive(const QPointF &position) { - const double width = timelineScene()->width(); + const double width = abstractScrollGraphicsScene()->width(); if (position.x() > width) { callSetClampedXPosition(width - (rect().width() / 2) - 1); @@ -104,7 +103,7 @@ void TimelineFrameHandle::setPositionInteractive(const QPointF &position) } else { callSetClampedXPosition(position.x() - rect().width() / 2); const qreal frame = std::round(mapFromSceneToFrame(rect().center().x())); - timelineScene()->commitCurrentFrame(frame); + timelineGraphicsScene()->commitCurrentFrame(frame); } } @@ -128,6 +127,11 @@ TimelineFrameHandle *TimelineFrameHandle::asTimelineFrameHandle() return this; } +TimelineGraphicsScene *TimelineFrameHandle::timelineGraphicsScene() const +{ + return qobject_cast(abstractScrollGraphicsScene()); +} + void TimelineFrameHandle::scrollOffsetChanged() { setPosition(position()); @@ -182,7 +186,7 @@ void TimelineFrameHandle::paint(QPainter *painter, QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const { - for (auto *view : timelineScene()->views()) { + for (auto *view : abstractScrollGraphicsScene()->views()) { if (view->objectName() == "SceneView") { auto graphicsViewCoords = view->mapFromGlobal(pos); auto sceneCoords = view->mapToScene(graphicsViewCoords); @@ -195,7 +199,7 @@ QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const int TimelineFrameHandle::computeScrollSpeed() const { const double mouse = mapFromGlobal(QCursor::pos()).x(); - const double width = timelineScene()->width(); + const double width = abstractScrollGraphicsScene()->width(); const double acc = mouse > width ? mouse - width : double(TimelineConstants::sectionWidth) - mouse; @@ -216,7 +220,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x) const int minimumWidth = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset - rect().width() / 2; const int maximumWidth = minimumWidth - + timelineScene()->rulerDuration() * timelineScene()->rulerScaling() + + abstractScrollGraphicsScene()->rulerDuration() * abstractScrollGraphicsScene()->rulerScaling() - scrollOffset(); setClampedXPosition(x, minimumWidth, maximumWidth); @@ -225,7 +229,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x) // Auto scroll when dragging playhead out of bounds. void TimelineFrameHandle::scrollOutOfBounds() { - const double width = timelineScene()->width(); + const double width = abstractScrollGraphicsScene()->width(); const double mouse = mapFromGlobal(QCursor::pos()).x(); if (mouse > width) @@ -236,14 +240,14 @@ void TimelineFrameHandle::scrollOutOfBounds() void TimelineFrameHandle::scrollOutOfBoundsMax() { - const double width = timelineScene()->width(); + const double width = abstractScrollGraphicsScene()->width(); if (QApplication::mouseButtons() == Qt::LeftButton) { - const double frameWidth = timelineScene()->rulerScaling(); + const double frameWidth = abstractScrollGraphicsScene()->rulerScaling(); const double upperThreshold = width - frameWidth; if (rect().center().x() > upperThreshold) { - timelineScene()->setScrollOffset(computeScrollSpeed()); - timelineScene()->invalidateScrollbar(); + abstractScrollGraphicsScene()->setScrollOffset(computeScrollSpeed()); + abstractScrollGraphicsScene()->invalidateScrollbar(); } callSetClampedXPosition(width - (rect().width() / 2) - 1); @@ -253,8 +257,8 @@ void TimelineFrameHandle::scrollOutOfBoundsMax() callSetClampedXPosition(width - (rect().width() / 2) - 1); const int frame = std::floor(mapFromSceneToFrame(rect().center().x())); - const int ef = timelineScene()->endFrame(); - timelineScene()->commitCurrentFrame(frame <= ef ? frame : ef); + const int ef = abstractScrollGraphicsScene()->endFrame(); + timelineGraphicsScene()->commitCurrentFrame(frame <= ef ? frame : ef); } } @@ -264,11 +268,11 @@ void TimelineFrameHandle::scrollOutOfBoundsMin() auto offset = computeScrollSpeed(); if (offset >= 0) - timelineScene()->setScrollOffset(offset); + abstractScrollGraphicsScene()->setScrollOffset(offset); else - timelineScene()->setScrollOffset(0); + abstractScrollGraphicsScene()->setScrollOffset(0); - timelineScene()->invalidateScrollbar(); + abstractScrollGraphicsScene()->invalidateScrollbar(); callSetClampedXPosition(TimelineConstants::sectionWidth); m_timer.start(); @@ -278,7 +282,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin() int frame = mapFromSceneToFrame(rect().center().x()); - const int sframe = timelineScene()->startFrame(); + const int sframe = abstractScrollGraphicsScene()->startFrame(); if (frame != sframe) { const qreal framePos = mapFromFrameToScene(frame); @@ -287,7 +291,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin() frame++; } - timelineScene()->commitCurrentFrame(frame >= sframe ? frame : sframe); + timelineGraphicsScene()->commitCurrentFrame(frame >= sframe ? frame : sframe); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelineitem.h index 9d8bd9493e5..22832f0d0d0 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineitem.h @@ -35,6 +35,8 @@ QT_FORWARD_DECLARE_CLASS(QPainterPath) namespace QmlDesigner { +class TimelineGraphicsScene; + class TimelineItem : public QGraphicsWidget { Q_OBJECT @@ -59,6 +61,8 @@ public: TimelineFrameHandle *asTimelineFrameHandle() override; + TimelineGraphicsScene *timelineGraphicsScene() const; + protected: void scrollOffsetChanged() override; QPainterPath shape() const override; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp index 1d700cf063f..12345a404e4 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp @@ -53,7 +53,7 @@ void TimelineMovableAbstractItem::itemDoubleClicked() int TimelineMovableAbstractItem::scrollOffset() const { - return timelineScene()->scrollOffset(); + return abstractScrollGraphicsScene()->scrollOffset(); } int TimelineMovableAbstractItem::xPosScrollOffset(int x) const @@ -63,7 +63,7 @@ int TimelineMovableAbstractItem::xPosScrollOffset(int x) const qreal TimelineMovableAbstractItem::mapFromFrameToScene(qreal x) const { - return TimelineConstants::sectionWidth + (x - timelineScene()->startFrame()) * rulerScaling() + return TimelineConstants::sectionWidth + (x - abstractScrollGraphicsScene()->startFrame()) * rulerScaling() - scrollOffset() + TimelineConstants::timelineLeftOffset; } @@ -71,8 +71,8 @@ qreal TimelineMovableAbstractItem::mapFromSceneToFrame(qreal x) const { return xPosScrollOffset(x - TimelineConstants::sectionWidth - TimelineConstants::timelineLeftOffset) - / timelineScene()->rulerScaling() - + timelineScene()->startFrame(); + / abstractScrollGraphicsScene()->rulerScaling() + + abstractScrollGraphicsScene()->startFrame(); } void TimelineMovableAbstractItem::mousePressEvent(QGraphicsSceneMouseEvent *event) @@ -133,7 +133,7 @@ TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem(QGraph qreal TimelineMovableAbstractItem::rulerScaling() const { - return static_cast(scene())->rulerScaling(); + return qobject_cast(scene())->rulerScaling(); } int TimelineMovableAbstractItem::type() const @@ -141,9 +141,9 @@ int TimelineMovableAbstractItem::type() const return Type; } -TimelineGraphicsScene *TimelineMovableAbstractItem::timelineScene() const +AbstractScrollGraphicsScene *TimelineMovableAbstractItem::abstractScrollGraphicsScene() const { - return static_cast(scene()); + return qobject_cast(scene()); } TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem() diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h index cf71397225e..199a78ad997 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h @@ -32,7 +32,7 @@ namespace QmlDesigner { -class TimelineGraphicsScene; +class AbstractScrollGraphicsScene; class TimelineKeyframeItem; class TimelineFrameHandle; @@ -75,7 +75,7 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void setClampedXPosition(qreal x, qreal min, qreal max); - TimelineGraphicsScene *timelineScene() const; + AbstractScrollGraphicsScene *abstractScrollGraphicsScene() const; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp index b62b64de9d7..5dc52bbc9b8 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp @@ -61,7 +61,7 @@ QPointF mapToItem(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *e return event->scenePos(); } -TimelineMoveTool::TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate) +TimelineMoveTool::TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate) : TimelineAbstractTool(scene, delegate) {} @@ -155,7 +155,7 @@ void TimelineMoveTool::mouseReleaseEvent(TimelineMovableAbstractItem *item, } } - scene()->timelineView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent", + scene()->abstractView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent", [this, current]() { current->commitPosition(mapToItem(current, current->rect().center())); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.h b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.h index c5c9702c44b..144c123b0e5 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.h @@ -41,7 +41,7 @@ class TimelineMoveTool : public TimelineAbstractTool Q_DECLARE_TR_FUNCTIONS(TimelineMoveTool) public: - explicit TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); + explicit TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate); void mousePressEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) override; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp index 1159619dce0..9aef430dea6 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp @@ -505,7 +505,7 @@ TimelineKeyframeItem::TimelineKeyframeItem(TimelinePropertyItem *parent, const M TimelineKeyframeItem::~TimelineKeyframeItem() { - timelineScene()->selectKeyframes(SelectionMode::Remove, {this}); + abstractScrollGraphicsScene()->selectKeyframes(SelectionMode::Remove, {this}); } void TimelineKeyframeItem::updateFrame() @@ -555,8 +555,8 @@ void TimelineKeyframeItem::commitPosition(const QPointF &point) void TimelineKeyframeItem::itemDoubleClicked() { - std::pair timelineRange = {timelineScene()->currentTimeline().startKeyframe(), - timelineScene()->currentTimeline().endKeyframe()}; + std::pair timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(), + timelineGraphicsScene()->currentTimeline().endKeyframe()}; editValue(m_frame, timelineRange, propertyItem()->propertyName()); } @@ -565,6 +565,11 @@ TimelineKeyframeItem *TimelineKeyframeItem::asTimelineKeyframeItem() return this; } +TimelineGraphicsScene *TimelineKeyframeItem::timelineGraphicsScene() const +{ + return qobject_cast(abstractScrollGraphicsScene()); +} + void TimelineKeyframeItem::blockUpdates() { s_blockUpdates = true; @@ -643,21 +648,21 @@ void TimelineKeyframeItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *even QMenu mainMenu; QAction *removeAction = mainMenu.addAction(tr("Delete Keyframe")); QObject::connect(removeAction, &QAction::triggered, [this]() { - timelineScene()->handleKeyframeDeletion(); + timelineGraphicsScene()->handleKeyframeDeletion(); }); QAction *editEasingAction = mainMenu.addAction(tr("Edit Easing Curve...")); QObject::connect(editEasingAction, &QAction::triggered, [this]() { - const QList keys = Utils::transform(timelineScene()->selectedKeyframes(), + const QList keys = Utils::transform(abstractScrollGraphicsScene()->selectedKeyframes(), &TimelineKeyframeItem::m_frame); - setEasingCurve(timelineScene(), keys); + setEasingCurve(timelineGraphicsScene(), keys); }); QAction *editValueAction = mainMenu.addAction(tr("Edit Keyframe...")); QObject::connect(editValueAction, &QAction::triggered, [this]() { - std::pair timelineRange = {timelineScene()->currentTimeline().startKeyframe(), - timelineScene()->currentTimeline().endKeyframe()}; + std::pair timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(), + timelineGraphicsScene()->currentTimeline().endKeyframe()}; editValue(m_frame, timelineRange, propertyItem()->propertyName()); }); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.h index 5d6666f4ed6..280b3bee548 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.h @@ -67,6 +67,7 @@ public: void itemDoubleClicked() override; TimelineKeyframeItem *asTimelineKeyframeItem() override; + TimelineGraphicsScene *timelineGraphicsScene() const; protected: bool hasManualBezier() const; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp index d8d34fe1d11..401b4ffee84 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp @@ -1,4 +1,4 @@ -/**************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ @@ -215,15 +215,7 @@ QVector TimelineSectionItem::keyframePositions() const return out; } -QTransform rotatationTransform(qreal degrees) -{ - QTransform transform; - transform.rotate(degrees); - - return transform; -} - -QPixmap rotateby90(const QPixmap &pixmap) +static QPixmap rotateby90(const QPixmap &pixmap) { QImage sourceImage = pixmap.toImage(); QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format()); @@ -550,6 +542,13 @@ void TimelineRulerSectionItem::invalidateRulerSize(const QmlTimeline &timeline) m_end = timeline.endKeyframe(); } +void TimelineRulerSectionItem::invalidateRulerSize(const qreal length) +{ + m_duration = length; + m_start = 0; + m_end = length; +} + void TimelineRulerSectionItem::setRulerScaleFactor(int scaling) { qreal blend = qreal(scaling) / 100.0; @@ -627,10 +626,12 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi static const QColor highlightColor = Theme::instance()->Theme::qmlDesignerButtonColor(); static const QColor handleColor = Theme::getColor(Theme::QmlDesigner_HighlightColor); + const int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene()); + painter->save(); painter->save(); painter->setRenderHint(QPainter::Antialiasing); - painter->translate(-timelineScene()->scrollOffset(), 0); + painter->translate(-scrollOffset, 0); painter->fillRect(TimelineConstants::sectionWidth, 0, size().width() - TimelineConstants::sectionWidth, @@ -666,11 +667,13 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi const int height = size().height() - 1; + + drawLine(painter, - TimelineConstants::sectionWidth + timelineScene()->scrollOffset() + TimelineConstants::sectionWidth + scrollOffset - TimelineConstants::timelineLeftOffset, height, - size().width() + timelineScene()->scrollOffset(), + size().width() + scrollOffset, height); QFont font = painter->font(); @@ -720,9 +723,12 @@ void TimelineRulerSectionItem::paintTicks(QPainter *painter) m_frameTick = qreal(deltaLine); + int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene()); + int height = size().height(); - const int totalWidth = (size().width() + timelineScene()->scrollOffset()) / m_scaling; - for (int i = timelineScene()->scrollOffset() / m_scaling; i < totalWidth; ++i) { + const int totalWidth = (size().width() + scrollOffset) / m_scaling; + + for (int i = scrollOffset / m_scaling; i < totalWidth; ++i) { if ((i % deltaText) == 0) { drawCenteredText(painter, TimelineConstants::sectionWidth + i * m_scaling, @@ -794,11 +800,11 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end) qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset - scrollOffset()); - qreal max = qreal(timelineScene()->rulerWidth() - TimelineConstants::sectionWidth + qreal max = qreal(abstractScrollGraphicsScene()->rulerWidth() - TimelineConstants::sectionWidth + rect().width()); - const qreal minFrameX = mapFromFrameToScene(timelineScene()->startFrame()); - const qreal maxFrameX = mapFromFrameToScene(timelineScene()->endFrame()); + const qreal minFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->startFrame() - 1); + const qreal maxFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->endFrame()+ 1000); if (min < minFrameX) min = minFrameX; @@ -811,7 +817,7 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end) else dragHandle(rect(), end, min, max); - timelineScene()->statusBarMessageChanged( + abstractScrollGraphicsScene()->statusBarMessageChanged( tr("Range from %1 to %2") .arg(qRound(mapFromSceneToFrame(rect().x()))) .arg(qRound(mapFromSceneToFrame(rect().width() + rect().x())))); @@ -975,7 +981,7 @@ void TimelineBarItem::dragCenter(QRectF rect, const QPointF &pos, qreal min, qre if (validateBounds(pos.x() - rect.topLeft().x())) { qreal targetX = pos.x() - m_pivot; if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping - qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); targetX = mapFromFrameToScene(snappedTargetFrame); } rect.moveLeft(targetX); @@ -999,7 +1005,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre if (validateBounds(pos.x() - left.topLeft().x())) { qreal targetX = pos.x() - m_pivot; if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping - qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); targetX = mapFromFrameToScene(snappedTargetFrame); } rect.setLeft(targetX); @@ -1015,7 +1021,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre if (validateBounds(pos.x() - right.topRight().x())) { qreal targetX = pos.x() - m_pivot; if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping - qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); targetX = mapFromFrameToScene(snappedTargetFrame); } rect.setRight(targetX); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h index 0cd8817a417..e5403bcb74f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h @@ -151,6 +151,7 @@ public: static TimelineRulerSectionItem *create(QGraphicsScene *parentScene, TimelineItem *parent); void invalidateRulerSize(const QmlTimeline &timeline); + void invalidateRulerSize(const qreal length); void setRulerScaleFactor(int scaling); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.cpp index a33f844b2e1..81d4bab8b3e 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.cpp @@ -38,7 +38,7 @@ namespace QmlDesigner { -TimelineSelectionTool::TimelineSelectionTool(TimelineGraphicsScene *scene, +TimelineSelectionTool::TimelineSelectionTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate) : TimelineAbstractTool(scene, delegate) , m_selectionRect(new QGraphicsRectItem) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.h b/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.h index 3485e087ec0..f635ea0530a 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineselectiontool.h @@ -45,7 +45,7 @@ enum class SelectionMode { New, Add, Remove, Toggle }; class TimelineSelectionTool : public TimelineAbstractTool { public: - explicit TimelineSelectionTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); + explicit TimelineSelectionTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate); ~TimelineSelectionTool() override; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.cpp index d84a4619998..472f85961cb 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.cpp @@ -38,7 +38,7 @@ namespace QmlDesigner { -TimelineToolDelegate::TimelineToolDelegate(TimelineGraphicsScene *scene) +TimelineToolDelegate::TimelineToolDelegate(AbstractScrollGraphicsScene *scene) : m_scene(scene) , m_start() , m_moveTool(new TimelineMoveTool(scene, this)) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.h b/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.h index f945c1a61b8..0a328386c03 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinetooldelegate.h @@ -38,7 +38,7 @@ class TimelineGraphicsScene; class TimelineToolDelegate { public: - TimelineToolDelegate(TimelineGraphicsScene* scene); + TimelineToolDelegate(AbstractScrollGraphicsScene* scene); QPointF startPoint() const; @@ -65,7 +65,7 @@ private: private: static const int dragDistance = 20; - TimelineGraphicsScene* m_scene; + AbstractScrollGraphicsScene* m_scene; QPointF m_start; From 454ff4c46bbfd0a068813bd6a0bb6929f1986e76 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 17 Jun 2020 19:10:35 +0200 Subject: [PATCH 39/46] QmlDesigner: First implementation of TransitionEditor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I14391e872f6a257a2cdf75e7d577de64c384c1fd Reviewed-by: Tim Jenssen Reviewed-by: Henning Gründl Reviewed-by: Leena Miettinen --- src/plugins/qmldesigner/CMakeLists.txt | 16 + .../transitioneditor/transitioneditor.pri | 35 + .../transitioneditor/transitioneditor.qrc | 4 + .../transitioneditorconstants.h | 37 + .../transitioneditorgraphicslayout.cpp | 172 ++++ .../transitioneditorgraphicslayout.h | 89 ++ .../transitioneditorgraphicsscene.cpp | 431 ++++++++++ .../transitioneditorgraphicsscene.h | 146 ++++ .../transitioneditorpropertyitem.cpp | 237 ++++++ .../transitioneditorpropertyitem.h | 73 ++ .../transitioneditorsectionitem.cpp | 803 ++++++++++++++++++ .../transitioneditorsectionitem.h | 145 ++++ .../transitioneditorsettingsdialog.cpp | 177 ++++ .../transitioneditorsettingsdialog.h | 66 ++ .../transitioneditorsettingsdialog.ui | 74 ++ .../transitioneditortoolbar.cpp | 342 ++++++++ .../transitioneditortoolbar.h | 92 ++ .../transitioneditor/transitioneditorview.cpp | 348 ++++++++ .../transitioneditor/transitioneditorview.h | 97 +++ .../transitioneditorwidget.cpp | 414 +++++++++ .../transitioneditor/transitioneditorwidget.h | 103 +++ .../transitioneditor/transitionform.cpp | 211 +++++ .../transitioneditor/transitionform.h | 57 ++ .../transitioneditor/transitionform.ui | 87 ++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 5 + src/plugins/qmldesigner/qmldesignerplugin.pro | 1 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 20 + 27 files changed, 4282 insertions(+) create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditor.qrc create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorconstants.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.ui create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitionform.h create mode 100644 src/plugins/qmldesigner/components/transitioneditor/transitionform.ui diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 866ca57cbc5..94a4f449a76 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -668,6 +668,22 @@ extend_qtc_plugin(QmlDesigner timelinewidget.cpp timelinewidget.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/transitioneditor + SOURCES + transitioneditorview.cpp transitioneditorview.h + transitioneditorwidget.cpp transitioneditorwidget.h + transitioneditortoolbar.cpp transitioneditortoolbar.h + transitioneditorgraphicsscene.cpp transitioneditorgraphicsscene.h + transitioneditorgraphicslayout.cpp transitioneditorgraphicslayout.h + transitioneditorsectionitem.cpp transitioneditorsectionitem.h + transitioneditorpropertyitem.cpp transitioneditorpropertyitem.h + transitioneditorsettingsdialog.cpp transitioneditorsettingsdialog.h + transitioneditorsettingsdialog.ui + transitionform.cpp transitionform.h + transitioneditor.qrc +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/curveeditor SOURCES diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri new file mode 100644 index 00000000000..8f9a9dec9eb --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri @@ -0,0 +1,35 @@ +QT *= qml quick core + +VPATH += $$PWD + +INCLUDEPATH += $$PWD + +SOURCES += \ + transitioneditorview.cpp \ + transitioneditorwidget.cpp \ + transitioneditortoolbar.cpp \ + transitioneditorgraphicsscene.cpp \ + transitioneditorgraphicslayout.cpp \ + transitioneditorsectionitem.cpp \ + transitioneditorpropertyitem.cpp \ + transitioneditorsettingsdialog.cpp \ + transitionform.cpp + +HEADERS += \ + transitioneditorconstants \ + transitioneditorview.h \ + transitioneditorwidget.h \ + transitioneditortoolbar.h \ + transitioneditorgraphicsscene.h \ + transitioneditorgraphicslayout.h \ + transitioneditorsectionitem.h \ + transitioneditorpropertyitem.h \ + transitioneditorsettingsdialog.h \ + transitionform.h + +RESOURCES += \ + transitioneditor.qrc + +FORMS += \ + transitioneditorsettingsdialog.ui \ + transitionform.ui diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.qrc b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.qrc new file mode 100644 index 00000000000..a2a962a6b83 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.qrc @@ -0,0 +1,4 @@ + + + + diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorconstants.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorconstants.h new file mode 100644 index 00000000000..c06249eacb1 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorconstants.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace QmlDesigner { +namespace TransitionEditorConstants { + +const int transitionEditorSectionItemUserType = QGraphicsItem::UserType + 6; +const int transitionEditorPropertyItemUserType = QGraphicsItem::UserType + 7; + +} // namespace TransitionEditorConstants +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp new file mode 100644 index 00000000000..02e1258dfd5 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** 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 "transitioneditorgraphicslayout.h" + +#include "timelinegraphicsscene.h" +#include "timelineplaceholder.h" +#include "timelinesectionitem.h" +#include "timelineview.h" +#include "transitioneditorsectionitem.h" + +#include + +#include + +namespace QmlDesigner { + +TransitionEditorGraphicsLayout::TransitionEditorGraphicsLayout(QGraphicsScene *scene, + TimelineItem *parent) + : TimelineItem(parent) + , m_layout(new QGraphicsLinearLayout) + , m_rulerItem(TimelineRulerSectionItem::create(scene, this)) + , m_placeholder1(TimelinePlaceholder::create(scene, this)) + , m_placeholder2(TimelinePlaceholder::create(scene, this)) +{ + m_layout->setOrientation(Qt::Vertical); + m_layout->setSpacing(0); + m_layout->setContentsMargins(0, 0, 0, 0); + + m_layout->addItem(m_rulerItem); + m_layout->addItem(m_placeholder1); + m_layout->addItem(m_placeholder2); + + setLayout(m_layout); + + setPos(QPointF(0, 0)); + + connect(m_rulerItem, + &TimelineRulerSectionItem::rulerClicked, + this, + &TransitionEditorGraphicsLayout::rulerClicked); +} + +TransitionEditorGraphicsLayout::~TransitionEditorGraphicsLayout() = default; + +double TransitionEditorGraphicsLayout::rulerWidth() const +{ + return m_rulerItem->preferredWidth(); +} + +double TransitionEditorGraphicsLayout::rulerScaling() const +{ + return m_rulerItem->rulerScaling(); +} + +double TransitionEditorGraphicsLayout::rulerDuration() const +{ + return m_rulerItem->rulerDuration(); +} + +double TransitionEditorGraphicsLayout::endFrame() const +{ + return m_rulerItem->endFrame(); +} + +void TransitionEditorGraphicsLayout::setWidth(int width) +{ + m_rulerItem->setSizeHints(width); + m_placeholder1->setMinimumWidth(width); + m_placeholder2->setMinimumWidth(width); + setPreferredWidth(width); + setMaximumWidth(width); +} + +void TransitionEditorGraphicsLayout::setTransition(const ModelNode &transition) +{ + m_layout->removeItem(m_rulerItem); + m_layout->removeItem(m_placeholder1); + m_layout->removeItem(m_placeholder2); + + m_rulerItem->setParentItem(nullptr); + m_placeholder1->setParentItem(nullptr); + m_placeholder2->setParentItem(nullptr); + + qDeleteAll(this->childItems()); + + m_rulerItem->setParentItem(this); + + qreal duration = 2000; + if (transition.isValid() && transition.hasAuxiliaryData("transitionDuration")) + duration = transition.auxiliaryData("transitionDuration").toDouble(); + + setDuration(duration); + m_layout->addItem(m_rulerItem); + + m_placeholder1->setParentItem(this); + m_layout->addItem(m_placeholder1); + + m_layout->invalidate(); + + if (transition.isValid() && !transition.directSubModelNodes().isEmpty()) { + for (const ModelNode ¶llel : transition.directSubModelNodes()) { + auto item = TransitionEditorSectionItem::create(parallel, this); + m_layout->addItem(item); + } + } + + m_placeholder2->setParentItem(this); + m_layout->addItem(m_placeholder2); + + if (auto *scene = timelineScene()) + if (auto *view = scene->timelineView()) + if (!transition.isValid() && view->isAttached()) + emit scaleFactorChanged(0); +} + +void TransitionEditorGraphicsLayout::setDuration(qreal duration) +{ + m_rulerItem->invalidateRulerSize(duration); +} + +void TransitionEditorGraphicsLayout::setRulerScaleFactor(int factor) +{ + m_rulerItem->setRulerScaleFactor(factor); +} + +void TransitionEditorGraphicsLayout::invalidate() +{ + m_layout->invalidate(); +} + +int TransitionEditorGraphicsLayout::maximumScrollValue() const +{ + const qreal w = this->geometry().width() - qreal(TimelineConstants::sectionWidth); + const qreal duration = m_rulerItem->rulerDuration() + m_rulerItem->rulerDuration() * 0.1; + const qreal maxr = m_rulerItem->rulerScaling() * duration - w; + return std::round(qMax(maxr, 0.0)); +} + +void TransitionEditorGraphicsLayout::activate() +{ + m_layout->activate(); +} + +TimelineRulerSectionItem *TransitionEditorGraphicsLayout::ruler() const +{ + return m_rulerItem; +} + +} // End namespace QmlDesigner. diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h new file mode 100644 index 00000000000..9362abffdfc --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 "timelineitem.h" + +QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout) + +namespace QmlDesigner { + +class TimelineItem; +class TimelineRulerSectionItem; +class TimelinePlaceholder; + +class ModelNode; + +class TransitionEditorGraphicsLayout : public TimelineItem +{ + Q_OBJECT + +signals: + void rulerClicked(const QPointF &pos); + + void scaleFactorChanged(int factor); + +public: + TransitionEditorGraphicsLayout(QGraphicsScene *scene, TimelineItem *parent = nullptr); + + ~TransitionEditorGraphicsLayout() override; + +public: + double rulerWidth() const; + + double rulerScaling() const; + + double rulerDuration() const; + + double endFrame() const; + + void setWidth(int width); + + void setTransition(const ModelNode &transition); + + void setDuration(qreal duration); + + void setRulerScaleFactor(int factor); + + void invalidate(); + + int maximumScrollValue() const; + + void activate(); + + TimelineRulerSectionItem *ruler() const; + +private: + QGraphicsLinearLayout *m_layout = nullptr; + + TimelineRulerSectionItem *m_rulerItem = nullptr; + + TimelinePlaceholder *m_placeholder1 = nullptr; + + TimelinePlaceholder *m_placeholder2 = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp new file mode 100644 index 00000000000..036fe173f5c --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** 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 "transitioneditorgraphicsscene.h" + +#include "transitioneditorgraphicslayout.h" +#include "transitioneditorpropertyitem.h" +#include "transitioneditorsectionitem.h" +#include "transitioneditortoolbar.h" +#include "transitioneditorview.h" +#include "transitioneditorwidget.h" + +#include "timelineactions.h" +#include "timelineitem.h" +#include "timelinemovableabstractitem.h" +#include "timelinemovetool.h" +#include "timelineplaceholder.h" +#include "timelinepropertyitem.h" +#include "timelinesectionitem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +static int deleteKey() +{ + if (Utils::HostOsInfo::isMacHost()) + return Qt::Key_Backspace; + + return Qt::Key_Delete; +} + +TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWidget *parent) + : AbstractScrollGraphicsScene(parent) + , m_parent(parent) + , m_layout(new TransitionEditorGraphicsLayout(this)) + , m_tools(this) +{ + addItem(m_layout); + + setSceneRect(m_layout->geometry()); + + connect(m_layout, &QGraphicsWidget::geometryChanged, this, [this]() { + auto rect = m_layout->geometry(); + + setSceneRect(rect); + + if (auto *gview = graphicsView()) + gview->setSceneRect(rect.adjusted(0, TimelineConstants::rulerHeight, 0, 0)); + + if (auto *rview = rulerView()) + rview->setSceneRect(rect); + }); + + auto changeScale = [this](int factor) { + transitionEditorWidget()->changeScaleFactor(factor); + setRulerScaling(qreal(factor)); + }; + connect(m_layout, &TransitionEditorGraphicsLayout::scaleFactorChanged, changeScale); +} + +TransitionEditorGraphicsScene::~TransitionEditorGraphicsScene() +{ + QSignalBlocker block(this); + qDeleteAll(items()); +} + +void TransitionEditorGraphicsScene::invalidateScrollbar() +{ + double max = m_layout->maximumScrollValue(); + transitionEditorWidget()->setupScrollbar(0, max, scrollOffset()); + if (scrollOffset() > max) + setScrollOffset(max); +} + +void TransitionEditorGraphicsScene::onShow() +{ + emit m_layout->scaleFactorChanged(0); +} + +void TransitionEditorGraphicsScene::setTransition(const ModelNode &transition) +{ + clearSelection(); + m_layout->setTransition(transition); +} + +void TransitionEditorGraphicsScene::clearTransition() +{ + m_transition = {}; + m_layout->setTransition({}); +} + +void TransitionEditorGraphicsScene::setWidth(int width) +{ + m_layout->setWidth(width); + invalidateScrollbar(); +} + +void TransitionEditorGraphicsScene::invalidateLayout() +{ + m_layout->invalidate(); +} + +void TransitionEditorGraphicsScene::setDuration(int duration) +{ + if (m_transition.isValid()) + m_transition.setAuxiliaryData("transitionDuration", duration); + m_layout->setDuration(duration); + qreal scaling = m_layout->rulerScaling(); + setRulerScaling(scaling); +} + +qreal TransitionEditorGraphicsScene::rulerScaling() const +{ + return m_layout->rulerScaling(); +} + +int TransitionEditorGraphicsScene::rulerWidth() const +{ + return m_layout->rulerWidth(); +} + +qreal TransitionEditorGraphicsScene::rulerDuration() const +{ + return m_layout->rulerDuration(); +} + +qreal TransitionEditorGraphicsScene::endFrame() const +{ + return m_layout->endFrame(); +} + +qreal TransitionEditorGraphicsScene::startFrame() const +{ + return 0; +} + +qreal TransitionEditorGraphicsScene::mapToScene(qreal x) const +{ + return TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset + + (x - startFrame()) * rulerScaling() - scrollOffset(); +} + +qreal TransitionEditorGraphicsScene::mapFromScene(qreal x) const +{ + auto xPosOffset = (x - TimelineConstants::sectionWidth - TimelineConstants::timelineLeftOffset) + + scrollOffset(); + + return xPosOffset / rulerScaling() + startFrame(); +} + +void TransitionEditorGraphicsScene::setRulerScaling(int scaleFactor) +{ + m_layout->setRulerScaleFactor(scaleFactor); + + setScrollOffset(0); + invalidateSections(); + invalidateScrollbar(); + update(); +} + +void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &target) +{ + if (!target.isValid()) + return; + + bool found = false; + + const QList items = m_layout->childItems(); + for (auto child : items) + TimelineSectionItem::updateDataForTarget(child, target, &found); + + if (!found) + invalidateScene(); + + clearSelection(); + invalidateLayout(); +} + +void TransitionEditorGraphicsScene::invalidateScene() +{ + invalidateScrollbar(); +} + +void TransitionEditorGraphicsScene::invalidateCurrentValues() +{ + const QList constItems = items(); + for (auto item : constItems) + TimelinePropertyItem::updateTextEdit(item); +} + +QGraphicsView *TransitionEditorGraphicsScene::graphicsView() const +{ + const QList constViews = views(); + for (auto *v : constViews) + if (v->objectName() == "SceneView") + return v; + + return nullptr; +} + +QGraphicsView *TransitionEditorGraphicsScene::rulerView() const +{ + const QList constViews = views(); + for (auto *v : constViews) + if (v->objectName() == "RulerView") + return v; + + return nullptr; +} + +QRectF TransitionEditorGraphicsScene::selectionBounds() const +{ + QRectF bbox; + + return bbox; +} + +void TransitionEditorGraphicsScene::clearSelection() +{ + if (m_selectedProperty) + m_selectedProperty->update(); + + m_selectedProperty = nullptr; + AbstractScrollGraphicsScene::clearSelection(); +} + +QList TransitionEditorGraphicsScene::itemsAt(const QPointF &pos) +{ + QTransform transform; + + if (auto *gview = graphicsView()) + transform = gview->transform(); + + return items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder, transform); +} + +void TransitionEditorGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos())); + + m_tools.mousePressEvent(topItem, event); + QGraphicsScene::mousePressEvent(event); +} + +void TransitionEditorGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos())); + m_tools.mouseMoveEvent(topItem, event); + QGraphicsScene::mouseMoveEvent(event); +} + +void TransitionEditorGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos())); + /* The tool has handle the event last. */ + QGraphicsScene::mouseReleaseEvent(event); + m_tools.mouseReleaseEvent(topItem, event); +} + +void TransitionEditorGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos())); + m_tools.mouseDoubleClickEvent(topItem, event); + QGraphicsScene::mouseDoubleClickEvent(event); +} + +void TransitionEditorGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) +{ + if (qgraphicsitem_cast(focusItem())) { + keyEvent->ignore(); + QGraphicsScene::keyPressEvent(keyEvent); + return; + } + + if (keyEvent->modifiers().testFlag(Qt::ControlModifier)) { + QGraphicsScene::keyPressEvent(keyEvent); + } else { + switch (keyEvent->key()) { + case Qt::Key_Left: + emit scroll(TimelineUtils::Side::Left); + keyEvent->accept(); + break; + + case Qt::Key_Right: + emit scroll(TimelineUtils::Side::Right); + keyEvent->accept(); + break; + + default: + QGraphicsScene::keyPressEvent(keyEvent); + break; + } + } +} + +void TransitionEditorGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) +{ + if (qgraphicsitem_cast(focusItem())) { + keyEvent->ignore(); + QGraphicsScene::keyReleaseEvent(keyEvent); + return; + } + + QGraphicsScene::keyReleaseEvent(keyEvent); +} + +void TransitionEditorGraphicsScene::invalidateSections() +{ + const QList children = m_layout->childItems(); + for (auto child : children) + TransitionEditorSectionItem::updateData(child); + + clearSelection(); + invalidateLayout(); +} + +TransitionEditorView *TransitionEditorGraphicsScene::transitionEditorView() const +{ + return m_parent->transitionEditorView(); +} + +TransitionEditorWidget *TransitionEditorGraphicsScene::transitionEditorWidget() const +{ + return m_parent; +} + +TransitionEditorToolBar *TransitionEditorGraphicsScene::toolBar() const +{ + return transitionEditorWidget()->toolBar(); +} + +void TransitionEditorGraphicsScene::activateLayout() +{ + m_layout->activate(); +} + +AbstractView *TransitionEditorGraphicsScene::abstractView() const +{ + return transitionEditorView(); +} + +bool TransitionEditorGraphicsScene::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ShortcutOverride: + if (static_cast(event)->key() == deleteKey()) { + QGraphicsScene::keyPressEvent(static_cast(event)); + event->accept(); + return true; + } + Q_FALLTHROUGH(); + default: + return QGraphicsScene::event(event); + } +} + +ModelNode TransitionEditorGraphicsScene::transitionModelNode() const +{ + if (transitionEditorView()->isAttached()) { + const QString timelineId = transitionEditorWidget()->toolBar()->currentTransitionId(); + return transitionEditorView()->modelNodeForId(timelineId); + } + + return ModelNode(); +} + +TransitionEditorPropertyItem *TransitionEditorGraphicsScene::selectedPropertyItem() const +{ + return m_selectedProperty; +} + +void TransitionEditorGraphicsScene::setSelectedPropertyItem(TransitionEditorPropertyItem *item) +{ + if (m_selectedProperty) + m_selectedProperty->update(); + m_selectedProperty = item; + emit selectionChanged(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h new file mode 100644 index 00000000000..2f04c5b7298 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + + +#include + +#include + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout) +QT_FORWARD_DECLARE_CLASS(QComboBox) + +namespace QmlDesigner { + +class TransitionEditorView; +class TransitionEditorWidget; +class TransitionEditorToolBar; +class TransitionEditorGraphicsLayout; + +class TimelineRulerSectionItem; +class TimelineFrameHandle; +class TimelineAbstractTool; +class TimelineMoveTool; +class TimelineKeyframeItem; +class TimelinePlaceholder; +class TimelineToolBar; +class TransitionEditorPropertyItem; + +class TransitionEditorGraphicsScene : public AbstractScrollGraphicsScene +{ + Q_OBJECT + +signals: + void selectionChanged(); + + void scroll(const TimelineUtils::Side &side); + +public: + explicit TransitionEditorGraphicsScene(TransitionEditorWidget *parent); + + ~TransitionEditorGraphicsScene() override; + + void onShow(); + + void setTransition(const ModelNode &transition); + void clearTransition(); + + void setWidth(int width); + + void invalidateLayout(); + void setDuration(int duration); + + TransitionEditorView *transitionEditorView() const; + TransitionEditorWidget *transitionEditorWidget() const; + TransitionEditorToolBar *toolBar() const; + + qreal rulerScaling() const override; + int rulerWidth() const override; + qreal rulerDuration() const override; + qreal endFrame() const override; + qreal startFrame() const override; + + qreal mapToScene(qreal x) const; + qreal mapFromScene(qreal x) const; + + void setRulerScaling(int scaling); + + void invalidateSectionForTarget(const ModelNode &modelNode); + + void invalidateScene(); + void invalidateCurrentValues(); + void invalidateRecordButtonsStatus(); + + QGraphicsView *graphicsView() const; + QGraphicsView *rulerView() const; + + QRectF selectionBounds() const; + + void selectKeyframes(const SelectionMode &mode, const QList &items); + void clearSelection() override; + + void activateLayout(); + + AbstractView *abstractView() const override; + ModelNode transitionModelNode() const; + + TransitionEditorPropertyItem *selectedPropertyItem() const; + void setSelectedPropertyItem(TransitionEditorPropertyItem *item); + + void invalidateScrollbar() override; + +signals: + void statusBarMessageChanged(const QString &message); + +protected: + bool event(QEvent *event) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; + + void keyPressEvent(QKeyEvent *keyEvent) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + +private: + void invalidateSections(); + QList itemsAt(const QPointF &pos); + +private: + TransitionEditorWidget *m_parent = nullptr; + TransitionEditorGraphicsLayout *m_layout = nullptr; + ModelNode m_transition; + + int m_scrollOffset = 0; + TimelineToolDelegate m_tools; + TransitionEditorPropertyItem *m_selectedProperty = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.cpp new file mode 100644 index 00000000000..f5b7b45b05e --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** 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 "transitioneditorpropertyitem.h" + +#include "abstractview.h" +#include "timelineconstants.h" +#include "timelineicons.h" +#include "transitioneditorgraphicsscene.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +TransitionEditorPropertyItem *TransitionEditorPropertyItem::create( + const ModelNode &animation, TransitionEditorSectionItem *parent) +{ + auto item = new TransitionEditorPropertyItem(parent); + item->m_animation = animation; + + auto sectionItem = new QGraphicsWidget(item); + + sectionItem->setGeometry(0, + 0, + TimelineConstants::sectionWidth, + TimelineConstants::sectionHeight); + + sectionItem->setZValue(10); + sectionItem->setCursor(Qt::ArrowCursor); + + item->setToolTip(item->propertyName()); + item->resize(parent->size()); + + item->m_barItem = new TransitionEditorBarItem(item); + item->invalidateBar(); + + return item; +} + +int TransitionEditorPropertyItem::type() const +{ + return Type; +} + +void TransitionEditorPropertyItem::updateData() +{ + invalidateBar(); +} + +void TransitionEditorPropertyItem::updateParentData() +{ + TransitionEditorSectionItem::invalidateBar(parentItem()); +} + +bool TransitionEditorPropertyItem::isSelected() const +{ + return transitionEditorGraphicsScene()->selectedPropertyItem() == this; +} + +QString TransitionEditorPropertyItem::propertyName() const +{ + if (m_animation.isValid()) { + const QString propertyName = m_animation.variantProperty("property").value().toString(); + if (!propertyName.isEmpty()) + return propertyName; + return m_animation.variantProperty("properties").value().toString(); + } + return QString(); +} + +void TransitionEditorPropertyItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *, + QWidget *) +{ + painter->save(); + + static const QColor penColor = Theme::instance()->qmlDesignerBackgroundColorDarker(); + static const QColor textColor = Theme::getColor(Theme::PanelTextColorLight); + static const QColor backgroundColor = Theme::instance() + ->qmlDesignerBackgroundColorDarkAlternate(); + + painter->fillRect(0, 0, TimelineConstants::sectionWidth, size().height(), backgroundColor); + painter->fillRect(TimelineConstants::textIndentationProperties - 4, + 0, + TimelineConstants::sectionWidth - TimelineConstants::textIndentationProperties + + 4, + size().height(), + backgroundColor.darker(110)); + + painter->setPen(penColor); + + drawLine(painter, + TimelineConstants::sectionWidth - 1, + 0, + TimelineConstants::sectionWidth - 1, + size().height()); + + drawLine(painter, + TimelineConstants::textIndentationProperties - 4, + TimelineConstants::sectionHeight - 1, + size().width(), + TimelineConstants::sectionHeight - 1); + + painter->setPen(textColor); + + const QFontMetrics metrics(font()); + + const QString elidedText = metrics.elidedText(propertyName(), + Qt::ElideMiddle, + qreal(TimelineConstants::sectionWidth) * 2.0 / 3 + - TimelineConstants::textIndentationProperties, + 0); + + painter->drawText(TimelineConstants::textIndentationProperties, 12, elidedText); + + painter->restore(); +} + +void TransitionEditorPropertyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent * /*event */) {} + +TransitionEditorPropertyItem::TransitionEditorPropertyItem(TransitionEditorSectionItem *parent) + : TimelineItem(parent) +{ + setPreferredHeight(TimelineConstants::sectionHeight); + setMinimumHeight(TimelineConstants::sectionHeight); + setMaximumHeight(TimelineConstants::sectionHeight); +} + +TransitionEditorGraphicsScene *TransitionEditorPropertyItem::transitionEditorGraphicsScene() const +{ + return qobject_cast(scene()); +} + +void TransitionEditorPropertyItem::invalidateBar() +{ + qreal min = 0; + qreal max = 0; + + QTC_ASSERT(m_animation.isValid(), return ); + QTC_ASSERT(m_animation.hasParentProperty(), return ); + + const ModelNode parent = m_animation.parentProperty().parentModelNode(); + + for (const ModelNode &child : parent.directSubModelNodes()) + if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation")) + min = child.variantProperty("duration").value().toDouble(); + + max = m_animation.variantProperty("duration").value().toDouble() + min; + + const qreal sceneMin = m_barItem->mapFromFrameToScene(min); + + QRectF barRect(sceneMin, + 0, + (max - min) * m_barItem->rulerScaling(), + TimelineConstants::sectionHeight - 1); + + m_barItem->setRect(barRect); +} + +AbstractView *TransitionEditorPropertyItem::view() const +{ + return m_animation.view(); +} + +ModelNode TransitionEditorPropertyItem::propertyAnimation() const +{ + return m_animation; +} + +ModelNode TransitionEditorPropertyItem::pauseAnimation() const +{ + QTC_ASSERT(m_animation.isValid(), return {}); + QTC_ASSERT(m_animation.hasParentProperty(), return {}); + + const ModelNode parent = m_animation.parentProperty().parentModelNode(); + + for (const ModelNode &child : parent.directSubModelNodes()) + if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation")) + return child; + + return {}; +} + +void TransitionEditorPropertyItem::select() +{ + transitionEditorGraphicsScene()->setSelectedPropertyItem(this); + m_barItem->update(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h new file mode 100644 index 00000000000..aba42b599d8 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 "transitioneditorsectionitem.h" + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QLineEdit) + +namespace QmlDesigner { + +class TransitionEditorGraphicsScene; + +class TransitionEditorPropertyItem : public TimelineItem +{ + Q_OBJECT + +public: + enum { Type = TransitionEditorConstants::transitionEditorPropertyItemUserType }; + + static TransitionEditorPropertyItem *create(const ModelNode &animation, + TransitionEditorSectionItem *parent = nullptr); + int type() const override; + void updateData(); + void updateParentData(); + + bool isSelected() const; + QString propertyName() const; + void invalidateBar(); + AbstractView *view() const; + ModelNode propertyAnimation() const; + ModelNode pauseAnimation() const; + void select(); + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + +private: + TransitionEditorPropertyItem(TransitionEditorSectionItem *parent = nullptr); + TransitionEditorGraphicsScene *transitionEditorGraphicsScene() const; + + ModelNode m_animation; + TransitionEditorBarItem *m_barItem; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp new file mode 100644 index 00000000000..86442059d9c --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** 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 "transitioneditorsectionitem.h" +#include "transitioneditorgraphicsscene.h" +#include "transitioneditorpropertyitem.h" + +#include "timelineactions.h" +#include "timelineconstants.h" +#include "timelineicons.h" +#include "timelinepropertyitem.h" +#include "timelineutils.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +namespace QmlDesigner { + +static void scaleDuration(const ModelNode &node, qreal s) +{ + if (node.hasVariantProperty("duration")) { + qreal old = node.variantProperty("duration").value().toDouble(); + node.variantProperty("duration").setValue(qRound(old * s)); + } +} + +static void moveDuration(const ModelNode &node, qreal s) +{ + if (node.hasVariantProperty("duration")) { + qreal old = node.variantProperty("duration").value().toDouble(); + node.variantProperty("duration").setValue(old + s); + } +} + +class ClickDummy : public TimelineItem +{ +public: + explicit ClickDummy(TransitionEditorSectionItem *parent) + : TimelineItem(parent) + { + setGeometry(0, 0, TimelineConstants::sectionWidth, TimelineConstants::sectionHeight); + + setZValue(10); + setCursor(Qt::ArrowCursor); + } + +protected: + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override + { + scene()->sendEvent(parentItem(), event); + } + void mousePressEvent(QGraphicsSceneMouseEvent *event) override + { + scene()->sendEvent(parentItem(), event); + } + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override + { + scene()->sendEvent(parentItem(), event); + } + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override + { + scene()->sendEvent(parentItem(), event); + } +}; + +TransitionEditorSectionItem::TransitionEditorSectionItem(TimelineItem *parent) + : TimelineItem(parent) +{} + +TransitionEditorSectionItem *TransitionEditorSectionItem::create(const ModelNode &animation, + TimelineItem *parent) +{ + auto item = new TransitionEditorSectionItem(parent); + + ModelNode target; + + if (animation.isValid()) { + const QList propertyAnimations = animation.subModelNodesOfType( + "QtQuick.PropertyAnimation"); + + for (const ModelNode &child : propertyAnimations) { + if (child.hasBindingProperty("target")) + target = child.bindingProperty("target").resolveToModelNode(); + } + } + + item->m_targetNode = target; + item->m_animationNode = animation; + item->createPropertyItems(); + + if (target.isValid()) + item->setToolTip(target.id()); + + item->m_dummyItem = new ClickDummy(item); + item->m_dummyItem->update(); + + item->m_barItem = new TransitionEditorBarItem(item); + item->invalidateBar(); + item->invalidateHeight(); + + return item; +} + +void TransitionEditorSectionItem::invalidateBar() +{ + qreal min = std::numeric_limits::max(); + qreal max = 0; + + if (!m_animationNode.isValid()) + return; + + for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) { + qreal locMin = 0; + qreal locMax = 0; + + for (const ModelNode &child : sequential.directSubModelNodes()) { + if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PropertyAnimation")) + locMax = child.variantProperty("duration").value().toDouble(); + else if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation")) + locMin = child.variantProperty("duration").value().toDouble(); + } + + locMax = locMax + locMin; + + min = qMin(min, locMin); + max = qMax(max, locMax); + } + + const qreal sceneMin = m_barItem->mapFromFrameToScene(min); + + QRectF barRect(sceneMin, + 0, + (max - min) * m_barItem->rulerScaling(), + TimelineConstants::sectionHeight - 1); + + m_barItem->setRect(barRect); +} + +int TransitionEditorSectionItem::type() const +{ + return Type; +} + +void TransitionEditorSectionItem::updateData(QGraphicsItem *item) +{ + if (auto sectionItem = qgraphicsitem_cast(item)) + sectionItem->updateData(); +} + +void TransitionEditorSectionItem::invalidateBar(QGraphicsItem *item) +{ + if (auto sectionItem = qgraphicsitem_cast(item)) + sectionItem->invalidateBar(); +} + +void TransitionEditorSectionItem::updateDataForTarget(QGraphicsItem *item, + const ModelNode &target, + bool *b) +{ + if (!target.isValid()) + return; + + if (auto sectionItem = qgraphicsitem_cast(item)) { + if (sectionItem->m_targetNode == target) { //TODO update animation node + sectionItem->updateData(); + if (b) + *b = true; + } + } +} + +void TransitionEditorSectionItem::moveAllDurations(qreal offset) +{ + for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) { + for (const ModelNode &child : sequential.directSubModelNodes()) { + if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation")) + moveDuration(child, offset); + } + } +} + +void TransitionEditorSectionItem::scaleAllDurations(qreal scale) +{ + for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) { + for (const ModelNode &child : sequential.directSubModelNodes()) { + if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PropertyAnimation")) + scaleDuration(child, scale); + } + } +} + +qreal TransitionEditorSectionItem::firstFrame() +{ + return 0; + //if (!m_timeline.isValid()) + //return 0; + + //return m_timeline.minActualKeyframe(m_targetNode); +} + +AbstractView *TransitionEditorSectionItem::view() const +{ + return m_animationNode.view(); +} + +bool TransitionEditorSectionItem::isSelected() const +{ + return m_targetNode.isValid() && m_targetNode.isSelected(); +} + +ModelNode TransitionEditorSectionItem::targetNode() const +{ + return m_targetNode; +} + +static QPixmap rotateby90(const QPixmap &pixmap) +{ + QImage sourceImage = pixmap.toImage(); + QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format()); + + for (int x = 0; x < pixmap.width(); x++) + for (int y = 0; y < pixmap.height(); y++) + destImage.setPixel(y, x, sourceImage.pixel(x, y)); + + QPixmap result = QPixmap::fromImage(destImage); + + result.setDevicePixelRatio(pixmap.devicePixelRatio()); + + return result; +} + +static int devicePixelHeight(const QPixmap &pixmap) +{ + return pixmap.height() / pixmap.devicePixelRatioF(); +} + +void TransitionEditorSectionItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem * /*option*/, + QWidget *) +{ + if (m_targetNode.isValid()) { + painter->save(); + + const QColor textColor = Theme::getColor(Theme::PanelTextColorLight); + const QColor penColor = Theme::instance()->qmlDesignerBackgroundColorDarker(); + QColor brushColor = Theme::getColor(Theme::BackgroundColorDark); + + int fillOffset = 0; + if (isSelected()) { + brushColor = Theme::getColor(Theme::QmlDesigner_HighlightColor); + fillOffset = 1; + } + + painter->fillRect(0, + 0, + TimelineConstants::sectionWidth, + TimelineConstants::sectionHeight - fillOffset, + brushColor); + painter->fillRect(TimelineConstants::sectionWidth, + 0, + size().width() - TimelineConstants::sectionWidth, + size().height(), + Theme::instance()->qmlDesignerBackgroundColorDarkAlternate()); + + painter->setPen(penColor); + drawLine(painter, + TimelineConstants::sectionWidth - 1, + 0, + TimelineConstants::sectionWidth - 1, + size().height() - 1); + drawLine(painter, + TimelineConstants::sectionWidth, + TimelineConstants::sectionHeight - 1, + size().width(), + TimelineConstants::sectionHeight - 1); + + static const QPixmap arrow = Theme::getPixmap("down-arrow"); + + static const QPixmap arrow90 = rotateby90(arrow); + + const QPixmap rotatedArrow = collapsed() ? arrow90 : arrow; + + const int textOffset = QFontMetrics(font()).ascent() + + (TimelineConstants::sectionHeight - QFontMetrics(font()).height()) + / 2; + + painter->drawPixmap(collapsed() ? 6 : 4, + (TimelineConstants::sectionHeight - devicePixelHeight(rotatedArrow)) / 2, + rotatedArrow); + + painter->setPen(textColor); + + QFontMetrics fm(painter->font()); + const QString elidedId = fm.elidedText(m_targetNode.id(), + Qt::ElideMiddle, + TimelineConstants::sectionWidth + - TimelineConstants::textIndentationSections); + painter->drawText(TimelineConstants::textIndentationSections, textOffset, elidedId); + + painter->restore(); + } +} + +void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->pos().y() > TimelineConstants::sectionHeight + || event->pos().x() < TimelineConstants::textIndentationSections) { + TimelineItem::mouseDoubleClickEvent(event); + return; + } + + if (event->button() == Qt::LeftButton) { + event->accept(); + toggleCollapsed(); + } +} + +void TransitionEditorSectionItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->pos().y() > TimelineConstants::sectionHeight) { + TimelineItem::mousePressEvent(event); + return; + } + + if (event->button() == Qt::LeftButton) + event->accept(); +} + +void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->pos().y() > TimelineConstants::sectionHeight) { + TimelineItem::mouseReleaseEvent(event); + return; + } + + if (event->button() != Qt::LeftButton) + return; + + event->accept(); + + if (event->pos().x() > TimelineConstants::textIndentationSections + && event->button() == Qt::LeftButton) { + if (m_targetNode.isValid()) + m_targetNode.view()->setSelectedModelNode(m_targetNode); + } else { + toggleCollapsed(); + } + update(); +} + +void TransitionEditorSectionItem::resizeEvent(QGraphicsSceneResizeEvent *event) +{ + TimelineItem::resizeEvent(event); + + for (auto child : propertyItems()) { + TransitionEditorPropertyItem *item = static_cast(child); + item->resize(size().width(), TimelineConstants::sectionHeight); + } +} + +void TransitionEditorSectionItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *) {} + +void TransitionEditorSectionItem::updateData() +{ + invalidateBar(); + resize(rulerWidth(), size().height()); + invalidateProperties(); + update(); +} + +const QList TransitionEditorSectionItem::propertyItems() const +{ + QList list; + + const QList children = childItems(); + for (auto child : children) { + if (m_barItem != child && m_dummyItem != child) + list.append(child); + } + + return list; +} + +void TransitionEditorSectionItem::invalidateHeight() +{ + int height = 0; + bool visible = true; + + if (collapsed()) { + height = TimelineConstants::sectionHeight; + visible = false; + } else { + const QList propertyAnimations = m_animationNode.subModelNodesOfType( + "QtQuick.PropertyAnimation"); + + height = TimelineConstants::sectionHeight + + propertyAnimations.count() * TimelineConstants::sectionHeight; + visible = true; + } + + for (auto child : propertyItems()) + child->setVisible(visible); + + setPreferredHeight(height); + setMinimumHeight(height); + setMaximumHeight(height); + + auto transitionScene = qobject_cast(scene()); + transitionScene->activateLayout(); +} + +void TransitionEditorSectionItem::createPropertyItems() +{ + int yPos = TimelineConstants::sectionHeight; + const QList propertyAnimations = m_animationNode.subModelNodesOfType( + "QtQuick.PropertyAnimation"); + for (const auto &anim : propertyAnimations) { + auto item = TransitionEditorPropertyItem::create(anim, this); + item->setY(yPos); + yPos = yPos + TimelineConstants::sectionHeight; + } +} + +void TransitionEditorSectionItem::invalidateProperties() +{ + for (auto child : propertyItems()) { + delete child; + } + + createPropertyItems(); + + for (auto child : propertyItems()) { + TransitionEditorPropertyItem *item = static_cast(child); + item->updateData(); + item->resize(size().width(), TimelineConstants::sectionHeight); + } + invalidateHeight(); +} + +bool TransitionEditorSectionItem::collapsed() const +{ + return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded"); +} + +qreal TransitionEditorSectionItem::rulerWidth() const +{ + return static_cast(scene())->rulerWidth(); +} + +void TransitionEditorSectionItem::toggleCollapsed() +{ + QTC_ASSERT(m_targetNode.isValid(), return ); + + if (collapsed()) + m_targetNode.setAuxiliaryData("timeline_expanded", true); + else + m_targetNode.removeAuxiliaryData("timeline_expanded"); + + invalidateHeight(); +} + +TransitionEditorBarItem::TransitionEditorBarItem(TransitionEditorSectionItem *parent) + : TimelineMovableAbstractItem(parent) +{ + setAcceptHoverEvents(true); + setPen(Qt::NoPen); +} + +TransitionEditorBarItem::TransitionEditorBarItem(TransitionEditorPropertyItem *parent) + : TimelineMovableAbstractItem(parent) +{ + setAcceptHoverEvents(true); + setPen(Qt::NoPen); +} + +void TransitionEditorBarItem::itemMoved(const QPointF &start, const QPointF &end) +{ + if (isActiveHandle(Location::Undefined)) + dragInit(rect(), start); + + qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset + - scrollOffset()); + qreal max = qreal(abstractScrollGraphicsScene()->rulerWidth() - TimelineConstants::sectionWidth + + rect().width()); + + const qreal minFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->startFrame()); + const qreal maxFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->endFrame()); + + if (min < minFrameX) + min = minFrameX; + + if (max > maxFrameX) + max = maxFrameX; + + if (isActiveHandle(Location::Center)) + dragCenter(rect(), end, min, max); + else + dragHandle(rect(), end, min, max); + + emit abstractScrollGraphicsScene()->statusBarMessageChanged( + tr("Range from %1 to %2") + .arg(qRound(mapFromSceneToFrame(rect().x()))) + .arg(qRound(mapFromSceneToFrame(rect().width() + rect().x())))); +} + +void TransitionEditorBarItem::commitPosition(const QPointF & /*point*/) +{ + if (sectionItem() && sectionItem()->view()) { + if (m_handle != Location::Undefined) { + sectionItem() + ->view() + ->executeInTransaction("TransitionEditorBarItem::commitPosition", [this]() { + qreal scaleFactor = rect().width() / m_oldRect.width(); + + qreal moved = (rect().topLeft().x() - m_oldRect.topLeft().x()) / rulerScaling(); + qreal supposedFirstFrame = qRound(moved); + + sectionItem()->scaleAllDurations(scaleFactor); + sectionItem()->moveAllDurations(supposedFirstFrame); + sectionItem()->updateData(); + }); + } + } else if (propertyItem() && propertyItem()->view()) { + if (m_handle != Location::Undefined) { + propertyItem() + ->view() + ->executeInTransaction("TransitionEditorBarItem::commitPosition", [this]() { + qreal scaleFactor = rect().width() / m_oldRect.width(); + qreal moved = (rect().topLeft().x() - m_oldRect.topLeft().x()) / rulerScaling(); + qreal supposedFirstFrame = qRound(moved); + scaleDuration(propertyItem()->propertyAnimation(), scaleFactor); + moveDuration(propertyItem()->pauseAnimation(), supposedFirstFrame); + propertyItem()->updateData(); + propertyItem()->updateParentData(); + }); + } + } + + m_handle = Location::Undefined; + m_bounds = Location::Undefined; + m_pivot = 0.0; + m_oldRect = QRectF(); + scrollOffsetChanged(); +} + +void TransitionEditorBarItem::scrollOffsetChanged() +{ + if (sectionItem()) + sectionItem()->invalidateBar(); + else if (propertyItem()) + propertyItem()->invalidateBar(); +} + +void TransitionEditorBarItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + + QColor brushColor = Theme::getColor(Theme::QmlDesigner_HighlightColor); + QColor brushColorSection = Theme::getColor(Theme::QmlDesigner_HighlightColor).darker(120); + QColor penColor = Theme::getColor(Theme::QmlDesigner_HighlightColor).lighter(140); + + const QRectF itemRect = rect(); + + painter->save(); + painter->setClipRect(TimelineConstants::sectionWidth, + 0, + itemRect.width() + itemRect.x(), + itemRect.height()); + + if (sectionItem()) + painter->fillRect(itemRect, brushColorSection); + else + painter->fillRect(itemRect, brushColor); + + if (propertyItem() && propertyItem()->isSelected()) { + painter->setPen(penColor); + painter->drawRect(itemRect.adjusted(0, 0, 0, -1)); + } + + painter->restore(); +} + +void TransitionEditorBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + const auto p = event->pos(); + + QRectF left, right; + if (handleRects(rect(), left, right)) { + if (left.contains(p) || right.contains(p)) { + if (cursor().shape() != Qt::SizeHorCursor) + setCursor(QCursor(Qt::SizeHorCursor)); + } else if (rect().contains(p)) { + if (cursor().shape() != Qt::ClosedHandCursor) + setCursor(QCursor(Qt::ClosedHandCursor)); + } + } else { + if (rect().contains(p)) + setCursor(QCursor(Qt::ClosedHandCursor)); + } +} + +void TransitionEditorBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent * /*event*/) {} + +void TransitionEditorBarItem::mousePressEvent(QGraphicsSceneMouseEvent * /*event*/) +{ + if (propertyItem()) + propertyItem()->select(); +} + +TransitionEditorSectionItem *TransitionEditorBarItem::sectionItem() const +{ + return qgraphicsitem_cast(parentItem()); +} + +TransitionEditorPropertyItem *TransitionEditorBarItem::propertyItem() const +{ + return qgraphicsitem_cast(parentItem()); +} + +void TransitionEditorBarItem::dragInit(const QRectF &rect, const QPointF &pos) +{ + QRectF left, right; + m_oldRect = rect; + if (handleRects(rect, left, right)) { + if (left.contains(pos)) { + m_handle = Location::Left; + m_pivot = pos.x() - left.topLeft().x(); + } else if (right.contains(pos)) { + m_handle = Location::Right; + m_pivot = pos.x() - right.topRight().x(); + } else if (rect.contains(pos)) { + m_handle = Location::Center; + m_pivot = pos.x() - rect.topLeft().x(); + } + + } else { + if (rect.contains(pos)) { + m_handle = Location::Center; + m_pivot = pos.x() - rect.topLeft().x(); + } + } +} + +void TransitionEditorBarItem::dragCenter(QRectF rect, const QPointF &pos, qreal min, qreal max) +{ + if (validateBounds(pos.x() - rect.topLeft().x())) { + qreal targetX = pos.x() - m_pivot; + + if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); + targetX = mapFromFrameToScene(snappedTargetFrame); + } + rect.moveLeft(targetX); + if (rect.topLeft().x() < min) { + rect.moveLeft(min); + setOutOfBounds(Location::Left); + } else if (rect.topRight().x() > max) { + rect.moveRight(max); + setOutOfBounds(Location::Right); + } + setRect(rect); + } +} + +void TransitionEditorBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qreal max) +{ + QRectF left, right; + handleRects(rect, left, right); + + if (isActiveHandle(Location::Left)) { + if (validateBounds(pos.x() - left.topLeft().x())) { + qreal targetX = pos.x() - m_pivot; + if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); + targetX = mapFromFrameToScene(snappedTargetFrame); + } + rect.setLeft(targetX); + if (rect.left() < min) { + rect.setLeft(min); + setOutOfBounds(Location::Left); + } else if (rect.left() >= rect.right() - minimumBarWidth) + rect.setLeft(rect.right() - minimumBarWidth); + + setRect(rect); + } + } else if (isActiveHandle(Location::Right)) { + if (validateBounds(pos.x() - right.topRight().x())) { + qreal targetX = pos.x() - m_pivot; + if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping + qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX)); + targetX = mapFromFrameToScene(snappedTargetFrame); + } + rect.setRight(targetX); + if (rect.right() > max) { + rect.setRight(max); + setOutOfBounds(Location::Right); + } else if (rect.right() <= rect.left() + minimumBarWidth) + rect.setRight(rect.left() + minimumBarWidth); + + setRect(rect); + } + } +} + +bool TransitionEditorBarItem::handleRects(const QRectF &rect, QRectF &left, QRectF &right) const +{ + if (rect.width() < minimumBarWidth) + return false; + + const qreal handleSize = rect.height(); + + auto handleRect = QRectF(0, 0, handleSize, handleSize); + handleRect.moveCenter(rect.center()); + + handleRect.moveLeft(rect.left()); + left = handleRect; + + handleRect.moveRight(rect.right()); + right = handleRect; + + return true; +} + +bool TransitionEditorBarItem::isActiveHandle(Location location) const +{ + return m_handle == location; +} + +void TransitionEditorBarItem::setOutOfBounds(Location location) +{ + m_bounds = location; + update(); +} + +bool TransitionEditorBarItem::validateBounds(qreal distance) +{ + update(); + if (m_bounds == Location::Left) { + if (distance > m_pivot) + m_bounds = Location::Center; + return false; + + } else if (m_bounds == Location::Right) { + if (distance < m_pivot) + m_bounds = Location::Center; + return false; + } + return true; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h new file mode 100644 index 00000000000..22aa21dd288 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 "transitioneditorconstants.h" + +#include +#include + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QPainter) + +namespace QmlDesigner { + +class TransitionEditorSectionItem; +class TransitionEditorPropertyItem; + +class TransitionEditorBarItem : public TimelineMovableAbstractItem +{ + Q_DECLARE_TR_FUNCTIONS(TimelineBarItem) + + enum class Location { Undefined, Center, Left, Right }; + +public: + explicit TransitionEditorBarItem(TransitionEditorSectionItem *parent); + explicit TransitionEditorBarItem(TransitionEditorPropertyItem *parent); + + void itemMoved(const QPointF &start, const QPointF &end) override; + void commitPosition(const QPointF &point) override; + +protected: + void scrollOffsetChanged() override; + void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget = nullptr) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + +private: + TransitionEditorSectionItem *sectionItem() const; + TransitionEditorPropertyItem *propertyItem() const; + + void dragInit(const QRectF &rect, const QPointF &pos); + void dragCenter(QRectF rect, const QPointF &pos, qreal min, qreal max); + void dragHandle(QRectF rect, const QPointF &pos, qreal min, qreal max); + bool handleRects(const QRectF &rect, QRectF &left, QRectF &right) const; + bool isActiveHandle(Location location) const; + + void setOutOfBounds(Location location); + bool validateBounds(qreal pivot); + +private: + Location m_handle = Location::Undefined; + + Location m_bounds = Location::Undefined; + + qreal m_pivot = 0.0; + + QRectF m_oldRect; + + static constexpr qreal minimumBarWidth = 2.0 + * static_cast(TimelineConstants::sectionHeight); +}; + +class TransitionEditorSectionItem : public TimelineItem +{ + Q_OBJECT + +public: + enum { Type = TransitionEditorConstants::transitionEditorSectionItemUserType }; + + static TransitionEditorSectionItem *create(const ModelNode &animation, + TimelineItem *parent); + + void invalidateBar(); + + int type() const override; + + static void updateData(QGraphicsItem *item); + static void invalidateBar(QGraphicsItem *item); + static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b); + + void moveAllDurations(qreal offset); + void scaleAllDurations(qreal scale); + qreal firstFrame(); + AbstractView *view() const; + bool isSelected() const; + + ModelNode targetNode() const; + void updateData(); + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void resizeEvent(QGraphicsSceneResizeEvent *event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + +private: + void invalidateHeight(); + void invalidateProperties(); + bool collapsed() const; + qreal rulerWidth() const; + void toggleCollapsed(); + void createPropertyItems(); + const QList propertyItems() const; + + TransitionEditorSectionItem(TimelineItem *parent = nullptr); + ModelNode m_targetNode; + ModelNode m_animationNode; + + TransitionEditorBarItem *m_barItem; + TimelineItem *m_dummyItem; + +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.cpp new file mode 100644 index 00000000000..57993d7ac21 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** 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 "transitioneditorsettingsdialog.h" +#include "timelinesettingsdialog.h" +#include "transitioneditorview.h" +#include "ui_transitioneditorsettingsdialog.h" + +#include "timelineicons.h" +#include "timelinesettingsmodel.h" +#include "transitionform.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace QmlDesigner { + +static void deleteAllTabs(QTabWidget *tabWidget) +{ + while (tabWidget->count() > 0) { + QWidget *w = tabWidget->widget(0); + tabWidget->removeTab(0); + delete w; + } +} + +static ModelNode getTransitionFromTabWidget(QTabWidget *tabWidget) +{ + QWidget *w = tabWidget->currentWidget(); + if (w) + return qobject_cast(w)->transition(); + return QmlTimeline(); +} + +static void setTabForTransition(QTabWidget *tabWidget, const ModelNode &timeline) +{ + for (int i = 0; i < tabWidget->count(); ++i) { + QWidget *w = tabWidget->widget(i); + if (qobject_cast(w)->transition() == timeline) { + tabWidget->setCurrentIndex(i); + return; + } + } +} + +TransitionEditorSettingsDialog::TransitionEditorSettingsDialog(QWidget *parent, + class TransitionEditorView *view) + : QDialog(parent) + , ui(new Ui::TransitionEditorSettingsDialog) + , m_transitionEditorView(view) +{ + //m_timelineSettingsModel = new TimelineSettingsModel(this, view); + + ui->setupUi(this); + + auto *transitionCornerWidget = new QToolBar; + + auto *transitionAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(), + tr("Add Transition")); + auto *transitionRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(), + tr("Remove Transition")); + + connect(transitionAddAction, &QAction::triggered, this, [this]() { + setupTransitions(m_transitionEditorView->addNewTransition()); + }); + + connect(transitionRemoveAction, &QAction::triggered, this, [this]() { + ModelNode transition = getTransitionFromTabWidget(ui->timelineTab); + if (transition.isValid()) { + transition.destroy(); + setupTransitions({}); + } + }); + + transitionCornerWidget->addAction(transitionAddAction); + transitionCornerWidget->addAction(transitionRemoveAction); + + ui->timelineTab->setCornerWidget(transitionCornerWidget, Qt::TopRightCorner); + + setupTransitions({}); + + connect(ui->timelineTab, &QTabWidget::currentChanged, this, [this]() { + m_currentTransition = getTransitionFromTabWidget(ui->timelineTab); + }); +} + +void TransitionEditorSettingsDialog::setCurrentTransition(const ModelNode &timeline) +{ + m_currentTransition = timeline; + setTabForTransition(ui->timelineTab, m_currentTransition); +} + +TransitionEditorSettingsDialog::~TransitionEditorSettingsDialog() +{ + delete ui; +} + +void TransitionEditorSettingsDialog::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + /* ignore */ + break; + + default: + QDialog::keyPressEvent(event); + } +} + +void TransitionEditorSettingsDialog::setupTransitions(const ModelNode &newTransition) +{ + deleteAllTabs(ui->timelineTab); + + const QList &transitions = m_transitionEditorView->allTransitions(); + + if (transitions.isEmpty()) { + m_currentTransition = {}; + auto transitionForm = new TransitionForm(this); + transitionForm->setDisabled(true); + ui->timelineTab->addTab(transitionForm, tr("No Transition")); + return; + } + + for (const auto &transition : transitions) + addTransitionTab(transition); + + if (newTransition.isValid()) { + m_currentTransition = newTransition; + } else { + m_currentTransition = transitions.constFirst(); + } + + setTabForTransition(ui->timelineTab, m_currentTransition); +} + +void TransitionEditorSettingsDialog::addTransitionTab(const QmlTimeline &node) +{ + auto transitionForm = new TransitionForm(this); + ui->timelineTab->addTab(transitionForm, node.modelNode().displayName()); + transitionForm->setTransition(node); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.h new file mode 100644 index 00000000000..ef9c6ad0c49 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QSpinBox) + +namespace QmlDesigner { + +class TransitionForm; +class TransitionEditorView; + +namespace Ui { +class TransitionEditorSettingsDialog; +} + +class TransitionEditorSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TransitionEditorSettingsDialog(QWidget *parent, class TransitionEditorView *view); + void setCurrentTransition(const ModelNode &timeline); + ~TransitionEditorSettingsDialog() override; + +protected: + void keyPressEvent(QKeyEvent *event) override; + +private: + void setupTransitions(const ModelNode &node); + + void addTransitionTab(const QmlTimeline &node); + + Ui::TransitionEditorSettingsDialog *ui; + + TransitionEditorView *m_transitionEditorView; + ModelNode m_currentTransition; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.ui b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.ui new file mode 100644 index 00000000000..282c2c475e9 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsettingsdialog.ui @@ -0,0 +1,74 @@ + + + QmlDesigner::TransitionEditorSettingsDialog + + + + 0 + 0 + 519 + 582 + + + + Transition Settings + + + true + + + + + + -1 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + QmlDesigner::TransitionEditorSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QmlDesigner::TransitionEditorSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp new file mode 100644 index 00000000000..af19629234d --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** 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 "transitioneditortoolbar.h" +#include "transitioneditorgraphicsscene.h" + +#include "timelineconstants.h" + +#include "timelineicons.h" + +#include "timelineview.h" +#include "timelinewidget.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +static bool isSpacer(QObject *object) +{ + return object->property("spacer_widget").toBool(); +} + +static QWidget *createSpacer() +{ + QWidget *spacer = new QWidget(); + spacer->setProperty("spacer_widget", true); + return spacer; +} + +static int controlWidth(QToolBar *bar, QObject *control) +{ + QWidget *widget = nullptr; + + if (auto *action = qobject_cast(control)) + widget = bar->widgetForAction(action); + + if (widget == nullptr) + widget = qobject_cast(control); + + if (widget) + return widget->width(); + + return 0; +} + +static QAction *createAction(const Core::Id &id, + const QIcon &icon, + const QString &name, + const QKeySequence &shortcut) +{ + QString text = QString("%1 (%2)").arg(name).arg(shortcut.toString()); + + Core::Context context(TimelineConstants::C_QMLTIMELINE); + + auto *action = new QAction(icon, text); + auto *command = Core::ActionManager::registerAction(action, id, context); + command->setDefaultKeySequence(shortcut); + + return action; +} + +TransitionEditorToolBar::TransitionEditorToolBar(QWidget *parent) + : QToolBar(parent) + , m_grp() +{ + setContentsMargins(0, 0, 0, 0); + createLeftControls(); + createCenterControls(); + createRightControls(); +} + +void TransitionEditorToolBar::reset() {} + +int TransitionEditorToolBar::scaleFactor() const +{ + if (m_scale) + return m_scale->value(); + return 0; +} + +QString TransitionEditorToolBar::currentTransitionId() const +{ + return m_transitionComboBox->currentText(); +} + +void TransitionEditorToolBar::setBlockReflection(bool block) +{ + m_blockReflection = block; +} + +void TransitionEditorToolBar::updateComboBox(const ModelNode &root) +{ + if (root.isValid() && root.hasProperty("transitions")) { + NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions"); + if (transitions.isValid()) + for (const ModelNode &transition : transitions.directSubNodes()) + m_transitionComboBox->addItem(transition.id()); + } +} + +void TransitionEditorToolBar::setCurrentTransition(const ModelNode &transition) +{ + if (m_blockReflection) + return; + + if (transition.isValid()) { + m_transitionComboBox->clear(); + const ModelNode root = transition.view()->rootModelNode(); + updateComboBox(root); + m_transitionComboBox->setCurrentText(transition.id()); + } else { + m_transitionComboBox->clear(); + m_transitionComboBox->setCurrentText(""); + } +} + +void TransitionEditorToolBar::setDuration(qreal frame) +{ + auto text = QString::number(frame, 'f', 0); + m_duration->setText(text); +} + +void TransitionEditorToolBar::setScaleFactor(int factor) +{ + const QSignalBlocker blocker(m_scale); + m_scale->setValue(factor); +} + +void TransitionEditorToolBar::setActionEnabled(const QString &name, bool enabled) +{ + for (auto *action : actions()) + if (action->objectName() == name) + action->setEnabled(enabled); +} + +void TransitionEditorToolBar::createLeftControls() +{ + auto addActionToGroup = [&](QAction *action) { + addAction(action); + m_grp << action; + }; + + auto addWidgetToGroup = [&](QWidget *widget) { + addWidget(widget); + m_grp << widget; + }; + + auto addSpacingToGroup = [&](int width) { + auto *widget = new QWidget; + widget->setFixedWidth(width); + addWidget(widget); + m_grp << widget; + }; + + addSpacingToGroup(5); + + auto *settingsAction = createAction(TimelineConstants::C_SETTINGS, + TimelineIcons::ANIMATION.icon(), + tr("Transition Settings"), + QKeySequence(Qt::Key_S)); + connect(settingsAction, + &QAction::triggered, + this, + &TransitionEditorToolBar::settingDialogClicked); + + addActionToGroup(settingsAction); + + addWidgetToGroup(createSpacer()); + + m_transitionComboBox = new QComboBox(this); + addWidgetToGroup(m_transitionComboBox); + + connect(m_transitionComboBox, &QComboBox::currentTextChanged, this, [this]() { + emit currentTransitionChanged(m_transitionComboBox->currentText()); + }); +} + +static QLineEdit *createToolBarLineEdit(QWidget *parent) +{ + auto lineEdit = new QLineEdit(parent); + lineEdit->setStyleSheet("* { background-color: rgba(0, 0, 0, 0); }"); + lineEdit->setFixedWidth(48); + lineEdit->setAlignment(Qt::AlignCenter); + + QPalette pal = parent->palette(); + pal.setColor(QPalette::Text, Theme::instance()->color(Utils::Theme::PanelTextColorLight)); + lineEdit->setPalette(pal); + QValidator *validator = new QIntValidator(-100000, 100000, lineEdit); + lineEdit->setValidator(validator); + + return lineEdit; +} + +void TransitionEditorToolBar::createCenterControls() +{ + addSpacing(10); + + auto *curvePicker = createAction(TimelineConstants::C_CURVE_PICKER, + TimelineIcons::CURVE_EDITOR.icon(), + tr("Easing Curve Editor"), + QKeySequence(Qt::Key_C)); + + curvePicker->setObjectName("Easing Curve Editor"); + connect(curvePicker, &QAction::triggered, this, &TransitionEditorToolBar::openEasingCurveEditor); + addAction(curvePicker); + + addSpacing(10); + +#if 0 + addSeparator(); + + addSpacing(10); + + auto *curveEditor = new QAction(TimelineIcons::CURVE_PICKER.icon(), tr("Curve Editor")); + addAction(curveEditor); +#endif +} + +void TransitionEditorToolBar::createRightControls() +{ + auto *spacer = createSpacer(); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + addWidget(spacer); + + addSeparator(); + addSpacing(10); + + auto *zoomOut = createAction(TimelineConstants::C_ZOOM_OUT, + TimelineIcons::ZOOM_SMALL.icon(), + tr("Zoom Out"), + QKeySequence(QKeySequence::ZoomOut)); + + connect(zoomOut, &QAction::triggered, [this]() { + m_scale->setValue(m_scale->value() - m_scale->pageStep()); + }); + addAction(zoomOut); + + addSpacing(10); + + m_scale = new QSlider(this); + m_scale->setOrientation(Qt::Horizontal); + m_scale->setMaximumWidth(200); + m_scale->setMinimumWidth(100); + m_scale->setMinimum(0); + m_scale->setMaximum(100); + m_scale->setValue(0); + + connect(m_scale, &QSlider::valueChanged, this, &TransitionEditorToolBar::scaleFactorChanged); + addWidget(m_scale); + + addSpacing(10); + + auto *zoomIn = createAction(TimelineConstants::C_ZOOM_IN, + TimelineIcons::ZOOM_BIG.icon(), + tr("Zoom In"), + QKeySequence(QKeySequence::ZoomIn)); + + connect(zoomIn, &QAction::triggered, [this]() { + m_scale->setValue(m_scale->value() + m_scale->pageStep()); + }); + addAction(zoomIn); + + addSpacing(10); + + addSeparator(); + + m_duration = createToolBarLineEdit(this); + addWidget(m_duration); + + auto emitEndChanged = [this]() { emit durationChanged(m_duration->text().toInt()); }; + connect(m_duration, &QLineEdit::editingFinished, emitEndChanged); +} + +void TransitionEditorToolBar::addSpacing(int width) +{ + auto *widget = new QWidget; + widget->setFixedWidth(width); + addWidget(widget); +} + +void TransitionEditorToolBar::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + + int width = 0; + QWidget *spacer = nullptr; + for (auto *object : qAsConst(m_grp)) { + if (isSpacer(object)) + spacer = qobject_cast(object); + else + width += controlWidth(this, object); + } + + if (spacer) { + int spacerWidth = TimelineConstants::sectionWidth - width - 12; + spacer->setFixedWidth(spacerWidth > 0 ? spacerWidth : 0); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h new file mode 100644 index 00000000000..eba74021ca4 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditortoolbar.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 "animationcurvedialog.h" +#include "animationcurveeditormodel.h" + +#include + +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QObject) +QT_FORWARD_DECLARE_CLASS(QResizeEvent) +QT_FORWARD_DECLARE_CLASS(QSlider) +QT_FORWARD_DECLARE_CLASS(QWidget) + +namespace QmlDesigner { + +class TimelineWidget; + +class QmlTimeline; + +class TransitionEditorToolBar : public QToolBar +{ + Q_OBJECT + +signals: + void settingDialogClicked(); + + void scaleFactorChanged(int value); + void durationChanged(int value); + void currentTransitionChanged(const QString &name); + void openEasingCurveEditor(); + +public: + explicit TransitionEditorToolBar(QWidget *parent = nullptr); + + void reset(); + + int scaleFactor() const; + QString currentTransitionId() const; + + void setBlockReflection(bool block); + void setCurrentTransition(const ModelNode &transition); + void setDuration(qreal frame); + void setScaleFactor(int factor); + + void setActionEnabled(const QString &name, bool enabled); + + void updateComboBox(const ModelNode &root); +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + void createLeftControls(); + void createCenterControls(); + void createRightControls(); + void addSpacing(int width); + + QList m_grp; + + QComboBox *m_transitionComboBox = nullptr; + QSlider *m_scale = nullptr; + QLineEdit *m_duration = nullptr; + + bool m_blockReflection = false; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp new file mode 100644 index 00000000000..a7ead0afd26 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** 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 "transitioneditorview.h" + +#include "transitioneditortoolbar.h" +#include "transitioneditorwidget.h" + +#include "transitioneditorgraphicsscene.h" +#include "transitioneditorsettingsdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace QmlDesigner { + +TransitionEditorView::TransitionEditorView(QObject *parent) + : AbstractView(parent) + , m_transitionEditorWidget(nullptr) +{ + +} + +TransitionEditorView::~TransitionEditorView() = default; + +void TransitionEditorView::modelAttached(Model *model) +{ + AbstractView::modelAttached(model); + if (m_transitionEditorWidget) + m_transitionEditorWidget->init(); +} + +void TransitionEditorView::modelAboutToBeDetached(Model *model) +{ + m_transitionEditorWidget->reset(); + + AbstractView::modelAboutToBeDetached(model); +} + +void TransitionEditorView::nodeCreated(const ModelNode & /*createdNode*/) {} + +void TransitionEditorView::nodeAboutToBeRemoved(const ModelNode & /*removedNode*/) {} + +void TransitionEditorView::nodeRemoved(const ModelNode & removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags /*propertyChange*/) +{ + if (parentProperty.name() == "transitions") + widget()->updateData(removedNode); +} + +void TransitionEditorView::nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty & /*oldPropertyParent*/, + AbstractView::PropertyChangeFlags /*propertyChange*/) +{ + if (newPropertyParent.name() == "transitions") + asyncUpdate(node); + + const ModelNode parent = newPropertyParent.parentModelNode(); + + qDebug() << Q_FUNC_INFO << parent; + if (parent.isValid() && parent.metaInfo().isValid() + && parent.metaInfo().isSubclassOf("QtQuick.Transition")) { + asyncUpdate(parent); + } +} + +void TransitionEditorView::instancePropertyChanged( + const QList> & /*propertyList*/) +{ + +} + +void TransitionEditorView::variantPropertiesChanged( + const QList & /* propertyList */, + AbstractView::PropertyChangeFlags /*propertyChange*/) +{ + +} + +void TransitionEditorView::bindingPropertiesChanged( + const QList & /*propertyList */, + AbstractView::PropertyChangeFlags /* propertyChange */) +{ + +} + +void TransitionEditorView::selectedNodesChanged(const QList & /*selectedNodeList*/, + const QList & /*lastSelectedNodeList*/) +{ + +} + +void TransitionEditorView::propertiesAboutToBeRemoved( + const QList & /*propertyList */) +{ + +} + +void TransitionEditorView::propertiesRemoved(const QList &propertyList) +{ + for (const AbstractProperty &property : propertyList) { + if (property.name() == "transitions") + widget()->init(); + } +} + +bool TransitionEditorView::hasWidget() const +{ + return true; +} + +void TransitionEditorView::nodeIdChanged(const ModelNode &node, const QString &, const QString &) +{ + if (node.metaInfo().isValid() && node.metaInfo().isSubclassOf("QtQuick.Transition")) + widget()->init(); +} + +void TransitionEditorView::currentStateChanged(const ModelNode &) +{ + +} + +TransitionEditorWidget *TransitionEditorView::widget() const +{ + return m_transitionEditorWidget; +} + +void TransitionEditorView::registerActions() +{ + +} + +ModelNode TransitionEditorView::addNewTransition() +{ + QList states; + const ModelNode root = rootModelNode(); + + if (QmlVisualNode::isValidQmlVisualNode(root)) { + states = QmlVisualNode(root).states().allStates(); + } + + if (states.isEmpty()) { + Core::AsynchronousMessageBox::warning(tr("No States Defined"), + tr("There are no states defined in this component.")); + return {}; + } + + QHash idPropertyList; + + const QVector validProperties = {"int", "real", "double", "qreal", "color", "QColor"}; + + for (const QmlModelState &state : qAsConst(states)) { + for (const QmlPropertyChanges & change : state.propertyChanges()) { + QStringList locList; + const ModelNode target = change.target(); + const QString targetId = target.id(); + if (target.isValid() && target.hasMetaInfo()) { + for (const VariantProperty &property : change.modelNode().variantProperties()) { + TypeName typeName = target.metaInfo().propertyTypeName(property.name()); + + if (validProperties.contains(typeName)) + locList.append(QString::fromUtf8(property.name())); + } + if (idPropertyList.contains(targetId)) { + QStringList newlist = idPropertyList.value(targetId); + for (const QString &str :locList) + if (!newlist.contains(str)) + newlist.append(str); + idPropertyList.insert(targetId, newlist); + } else { + if (!locList.isEmpty()) + idPropertyList.insert(targetId, locList); + } + } + } + } + + ModelNode transition; + + if (!idPropertyList.isEmpty()) { + executeInTransaction( + " TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() { + transition = createModelNode("QtQuick.Transition", + 2, + 0, + {{ + "from", + "*", + }, + { + "to", + "*", + }}); + transition.setAuxiliaryData("transitionDuration", 2000); + transition.validId(); + root.nodeListProperty("transitions").reparentHere(transition); + + for (const QString &id : idPropertyList.keys()) { + ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation", + 2, + 12); + transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation); + for (const QString &property : idPropertyList.value(id)) { + ModelNode sequentialAnimation + = createModelNode("QtQuick.SequentialAnimation", 2, 12); + parallelAnimation.defaultNodeAbstractProperty().reparentHere( + sequentialAnimation); + + ModelNode pauseAnimation = createModelNode("QtQuick.PauseAnimation", + 2, + 12, + {{"duration", 50}}); + sequentialAnimation.defaultNodeAbstractProperty().reparentHere( + pauseAnimation); + + ModelNode propertyAnimation = createModelNode("QtQuick.PropertyAnimation", + 2, + 12, + {{"property", property}, + {"duration", 150}}); + propertyAnimation.bindingProperty("target").setExpression(id); + sequentialAnimation.defaultNodeAbstractProperty().reparentHere( + propertyAnimation); + } + } + }); + } + + if (m_transitionEditorWidget) + m_transitionEditorWidget->init(); + + return transition; +} + +TransitionEditorWidget *TransitionEditorView::createWidget() +{ + if (!m_transitionEditorWidget) + m_transitionEditorWidget = new TransitionEditorWidget(this); + + //auto *timelineContext = new TimelineContext(m_timelineWidget); + //Core::ICore::addContextObject(timelineContext); + + return m_transitionEditorWidget; +} + +WidgetInfo TransitionEditorView::widgetInfo() +{ + return createWidgetInfo(createWidget(), + nullptr, + "TransitionEditor", + WidgetInfo::BottomPane, + 0, + tr("Transition Editor")); +} + +void TransitionEditorView::openSettingsDialog() +{ + auto dialog = new TransitionEditorSettingsDialog(Core::ICore::dialogParent(), this); + + auto transition = widget()->graphicsScene()->transitionModelNode(); + if (transition.isValid()) + dialog->setCurrentTransition(transition); + + QObject::connect(dialog, &TransitionEditorSettingsDialog::rejected, [this, dialog]() { + widget()->init(); + dialog->deleteLater(); + }); + + QObject::connect(dialog, &TransitionEditorSettingsDialog::accepted, [this, dialog]() { + widget()->init(); + dialog->deleteLater(); + }); + + dialog->show(); +} + +const QList TransitionEditorView::allTransitions() const +{ + if (rootModelNode().isValid() && rootModelNode().hasProperty("transitions")) { + NodeAbstractProperty transitions = rootModelNode().nodeAbstractProperty("transitions"); + if (transitions.isValid()) + return transitions.directSubNodes(); + } + return {}; +} + +void TransitionEditorView::asyncUpdate(const ModelNode &transition) +{ + static bool updateTriggered = false; + + if (!updateTriggered && (transition.id() == widget()->toolBar()->currentTransitionId())) { + updateTriggered = true; + QTimer::singleShot(0, [this, transition]() { + widget()->updateData(transition); + updateTriggered = false; + }); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h new file mode 100644 index 00000000000..faa383c6215 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 "animationcurvedialog.h" +#include "animationcurveeditormodel.h" +#include "treeitem.h" + +#include + +#include + +namespace QmlDesigner { + +class TransitionEditorWidget; + +class TransitionEditorView : public AbstractView +{ + Q_OBJECT + +public: + explicit TransitionEditorView(QObject *parent = nullptr); + ~TransitionEditorView() override; + //Abstract View + WidgetInfo widgetInfo() override; + void modelAttached(Model *model) override; + void modelAboutToBeDetached(Model *model) override; + void nodeCreated(const ModelNode &createdNode) override; + void nodeAboutToBeRemoved(const ModelNode &removedNode) 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; + void instancePropertyChanged(const QList> &propertyList) override; + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + void selectedNodesChanged(const QList &selectedNodeList, + const QList &lastSelectedNodeList) override; + + void propertiesAboutToBeRemoved(const QList &propertyList) override; + void propertiesRemoved(const QList &propertyList) override; + + bool hasWidget() const override; + + void nodeIdChanged(const ModelNode &node, const QString &, const QString &) override; + + void currentStateChanged(const ModelNode &node) override; + + TransitionEditorWidget *widget() const; + + void insertKeyframe(const ModelNode &target, const PropertyName &propertyName); + + void registerActions(); + + ModelNode addNewTransition(); + + void openSettingsDialog(); + + const QList allTransitions() const; + + void asyncUpdate(const ModelNode &transition); + +private: + TransitionEditorWidget *createWidget(); + + TransitionEditorWidget *m_transitionEditorWidget = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp new file mode 100644 index 00000000000..5d27b4c9699 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** 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 "transitioneditorwidget.h" + +#include "transitioneditorgraphicsscene.h" +#include "transitioneditorpropertyitem.h" +#include "transitioneditortoolbar.h" +#include "transitioneditorview.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +class Eventfilter : public QObject +{ +public: + Eventfilter(QObject *parent) + : QObject(parent) + {} + + bool eventFilter(QObject *, QEvent *event) override + { + if (event->type() == QEvent::Wheel) { + event->accept(); + return true; + } + return false; + } +}; + +TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) + : QWidget() + , m_toolbar(new TransitionEditorToolBar(this)) + , m_rulerView(new QGraphicsView(this)) + , m_graphicsView(new QGraphicsView(this)) + , m_scrollbar(new QScrollBar(this)) + , m_statusBar(new QLabel(this)) + , m_transitionEditorView(view) + , m_graphicsScene(new TransitionEditorGraphicsScene(this)) + , m_addButton(new QPushButton(this)) + , m_onboardingContainer(new QWidget(this)) +{ + setWindowTitle(tr("Transition", "Title of transition view")); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + const QString css = Theme::replaceCssColors(QString::fromUtf8( + Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + + m_scrollbar->setStyleSheet(css); + m_scrollbar->setOrientation(Qt::Horizontal); + + QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(m_graphicsView->sizePolicy().hasHeightForWidth()); + + m_rulerView->setObjectName("RulerView"); + m_rulerView->setFixedHeight(TimelineConstants::rulerHeight); + m_rulerView->setAlignment(Qt::AlignLeft | Qt::AlignTop); + m_rulerView->viewport()->installEventFilter(new Eventfilter(this)); + m_rulerView->viewport()->setFocusPolicy(Qt::NoFocus); + m_rulerView->setStyleSheet(css); + m_rulerView->setFrameShape(QFrame::NoFrame); + m_rulerView->setFrameShadow(QFrame::Plain); + m_rulerView->setLineWidth(0); + m_rulerView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_rulerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_rulerView->setScene(graphicsScene()); + + m_graphicsView->setStyleSheet(css); + m_graphicsView->setObjectName("SceneView"); + m_graphicsView->setFrameShape(QFrame::NoFrame); + m_graphicsView->setFrameShadow(QFrame::Plain); + m_graphicsView->setLineWidth(0); + m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_graphicsView->setSizePolicy(sizePolicy1); + m_graphicsView->setScene(graphicsScene()); + m_graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop); + m_graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + + auto *scrollBarLayout = new QHBoxLayout; + scrollBarLayout->addSpacing(TimelineConstants::sectionWidth); + scrollBarLayout->addWidget(m_scrollbar); + + QMargins margins(0, 0, 0, QApplication::style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); + + auto *contentLayout = new QVBoxLayout; + contentLayout->setContentsMargins(margins); + contentLayout->addWidget(m_rulerView); + contentLayout->addWidget(m_graphicsView); + contentLayout->addLayout(scrollBarLayout); + contentLayout->addWidget(m_statusBar); + m_statusBar->setIndent(2); + m_statusBar->setFixedHeight(TimelineConstants::rulerHeight); + + auto *widgetLayout = new QVBoxLayout; + widgetLayout->setContentsMargins(0, 0, 0, 0); + widgetLayout->setSpacing(0); + widgetLayout->addWidget(m_toolbar); + widgetLayout->addWidget(m_addButton); + + m_addButton->setIcon(TimelineIcons::ADD_TIMELINE_TOOLBAR.icon()); + m_addButton->setToolTip(tr("Add Transition")); + m_addButton->setFlat(true); + m_addButton->setFixedSize(32, 32); + + widgetLayout->addWidget(m_onboardingContainer); + + auto *onboardingTopLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomLabel = new QLabel(m_onboardingContainer); + auto *onboardingBottomIcon = new QLabel(m_onboardingContainer); + + auto *onboardingLayout = new QVBoxLayout; + auto *onboardingSublayout = new QHBoxLayout; + auto *leftSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *rightSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + auto *topSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + auto *bottomSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); + + QString labelText = tr("This file does not contain transitions.

\ + To create an animation, add a transition by clicking the + button."); + onboardingTopLabel->setText(labelText); + onboardingTopLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + + m_onboardingContainer->setLayout(onboardingLayout); + onboardingLayout->setContentsMargins(0, 0, 0, 0); + onboardingLayout->setSpacing(0); + onboardingLayout->addSpacerItem(topSpacer); + onboardingLayout->addWidget(onboardingTopLabel); + onboardingLayout->addLayout(onboardingSublayout); + + onboardingSublayout->setContentsMargins(0, 0, 0, 0); + onboardingSublayout->setSpacing(0); + onboardingSublayout->addSpacerItem(leftSpacer); + + onboardingBottomLabel->setAlignment(Qt::AlignRight | Qt::AlignTop); + onboardingBottomLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomLabel); + onboardingBottomLabel->setText(tr("To edit the transition settings, click ")); + + onboardingBottomIcon->setAlignment(Qt::AlignLeft | Qt::AlignTop); + onboardingBottomIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + onboardingSublayout->addWidget(onboardingBottomIcon); + onboardingBottomIcon->setPixmap(TimelineIcons::ANIMATION.pixmap()); + + onboardingSublayout->addSpacerItem(rightSpacer); + onboardingLayout->addSpacerItem(bottomSpacer); + + widgetLayout->addLayout(contentLayout); + this->setLayout(widgetLayout); + + connectToolbar(); + + auto setScrollOffset = [this]() { graphicsScene()->setScrollOffset(m_scrollbar->value()); }; + connect(m_scrollbar, &QSlider::valueChanged, this, setScrollOffset); + + connect(graphicsScene(), + &TransitionEditorGraphicsScene::statusBarMessageChanged, + this, + [this](const QString &message) { m_statusBar->setText(message); }); + + connect(m_addButton, &QPushButton::clicked, this, [this]() { + m_transitionEditorView->addNewTransition(); + }); +} + +void TransitionEditorWidget::setTransitionActive(bool b) +{ + if (b) { + m_toolbar->setVisible(true); + m_graphicsView->setVisible(true); + m_rulerView->setVisible(true); + m_scrollbar->setVisible(true); + m_addButton->setVisible(false); + m_onboardingContainer->setVisible(false); + m_graphicsView->update(); + m_rulerView->update(); + } else { + m_toolbar->setVisible(false); + m_graphicsView->setVisible(false); + m_rulerView->setVisible(false); + m_scrollbar->setVisible(false); + m_addButton->setVisible(true); + m_onboardingContainer->setVisible(true); + } +} + +void TransitionEditorWidget::connectToolbar() +{ + connect(graphicsScene(), + &TransitionEditorGraphicsScene::selectionChanged, + this, + &TransitionEditorWidget::selectionChanged); + + connect(m_toolbar, + &TransitionEditorToolBar::openEasingCurveEditor, + this, + &TransitionEditorWidget::openEasingCurveEditor); + + connect(graphicsScene(), + &TransitionEditorGraphicsScene::scroll, + this, + &TransitionEditorWidget::scroll); + + auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); }; + connect(m_toolbar, &TransitionEditorToolBar::scaleFactorChanged, setRulerScaling); + + auto setDuration = [this](int end) { graphicsScene()->setDuration(end); }; + connect(m_toolbar, &TransitionEditorToolBar::durationChanged, setDuration); + + connect(m_toolbar, + &TransitionEditorToolBar::settingDialogClicked, + transitionEditorView(), + &TransitionEditorView::openSettingsDialog); + + connect(m_toolbar, + &TransitionEditorToolBar::currentTransitionChanged, + this, + [this](const QString &transitionName) { + const ModelNode transition = transitionEditorView()->modelNodeForId(transitionName); + if (transition.isValid()) { + m_graphicsScene->setTransition(transition); + } + }); +} + +void TransitionEditorWidget::changeScaleFactor(int factor) +{ + m_toolbar->setScaleFactor(factor); +} + +void TransitionEditorWidget::scroll(const TimelineUtils::Side &side) +{ + if (side == TimelineUtils::Side::Left) + m_scrollbar->setValue(m_scrollbar->value() - m_scrollbar->singleStep()); + else if (side == TimelineUtils::Side::Right) + m_scrollbar->setValue(m_scrollbar->value() + m_scrollbar->singleStep()); +} + +void TransitionEditorWidget::selectionChanged() +{ + if (graphicsScene()->selectedPropertyItem() != nullptr) + m_toolbar->setActionEnabled("Curve Picker", true); + else + m_toolbar->setActionEnabled("Curve Picker", false); +} + +void TransitionEditorWidget::contextHelp(const Core::IContext::HelpCallback &callback) const +{ + if (transitionEditorView()) + transitionEditorView()->contextHelp(callback); + else + callback({}); +} + +void TransitionEditorWidget::init() +{ + ModelNode root = transitionEditorView()->rootModelNode(); + ModelNode transition; + + if (root.isValid() && root.hasProperty("transitions")) { + NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions"); + if (transitions.isValid()) + transition = transitions.directSubNodes().first(); + } + + m_graphicsScene->setTransition(transition); + setTransitionActive(transition.isValid()); + + m_graphicsScene->setWidth(m_graphicsView->viewport()->width()); + + m_toolbar->setScaleFactor(0); + + m_toolbar->setCurrentTransition(transition); + + qreal duration = 2000; + if (transition.isValid() && transition.hasAuxiliaryData("transitionDuration")) + duration = transition.auxiliaryData("transitionDuration").toDouble(); + + m_toolbar->setDuration(duration); + + m_graphicsScene->setRulerScaling(0); +} + +void TransitionEditorWidget::updateData(const ModelNode &transition) +{ + if (!transition.isValid()) { + init(); + return; + } + + if (transition.metaInfo().isValid() + && transition.metaInfo().isSubclassOf("QtQuick.Transition")) { + if (transition.id() == m_toolbar->currentTransitionId()) { + m_graphicsScene->setTransition(transition); + } else { + m_toolbar->updateComboBox(transition.view()->rootModelNode()); + } + } +} + +void TransitionEditorWidget::reset() +{ + graphicsScene()->clearTransition(); + m_toolbar->reset(); + m_statusBar->clear(); +} + +TransitionEditorGraphicsScene *TransitionEditorWidget::graphicsScene() const +{ + return m_graphicsScene; +} + +TransitionEditorToolBar *TransitionEditorWidget::toolBar() const +{ + return m_toolbar; +} + +void TransitionEditorWidget::setupScrollbar(int min, int max, int current) +{ + bool b = m_scrollbar->blockSignals(true); + m_scrollbar->setMinimum(min); + m_scrollbar->setMaximum(max); + m_scrollbar->setValue(current); + m_scrollbar->setSingleStep((max - min) / 10); + m_scrollbar->blockSignals(b); +} + +void TransitionEditorWidget::showEvent(QShowEvent *event) +{ + Q_UNUSED(event) + graphicsScene()->setWidth(m_graphicsView->viewport()->width()); + graphicsScene()->invalidateLayout(); + graphicsScene()->invalidate(); + graphicsScene()->onShow(); +} + +void TransitionEditorWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + graphicsScene()->setWidth(m_graphicsView->viewport()->width()); +} + +TransitionEditorView *TransitionEditorWidget::transitionEditorView() const +{ + return m_transitionEditorView; +} + +void TransitionEditorWidget::openEasingCurveEditor() +{ + if (TransitionEditorPropertyItem *item = graphicsScene()->selectedPropertyItem()) { + QList animations; + animations.append(item->propertyAnimation()); + EasingCurveDialog::runDialog(animations, Core::ICore::dialogParent()); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h new file mode 100644 index 00000000000..97f57f1c2f6 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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 "timelineeditor/timelineutils.h" + +#include + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QComboBox) +QT_FORWARD_DECLARE_CLASS(QGraphicsView) +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QResizeEvent) +QT_FORWARD_DECLARE_CLASS(QScrollBar) +QT_FORWARD_DECLARE_CLASS(QShowEvent) +QT_FORWARD_DECLARE_CLASS(QString) +QT_FORWARD_DECLARE_CLASS(QPushButton) + +namespace QmlDesigner { + +class TransitionEditorView; +class TransitionEditorToolBar; +class TransitionEditorGraphicsScene; +class ModelNode; + +class TransitionEditorWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TransitionEditorWidget(TransitionEditorView *view); + void contextHelp(const Core::IContext::HelpCallback &callback) const; + + TransitionEditorGraphicsScene *graphicsScene() const; + TransitionEditorView *transitionEditorView() const; + TransitionEditorToolBar *toolBar() const; + + void init(); + void reset(); + + void setupScrollbar(int min, int max, int current); + void changeScaleFactor(int factor); + + void updateData(const ModelNode &transition); +public slots: + void selectionChanged(); + void scroll(const TimelineUtils::Side &side); + +protected: + void showEvent(QShowEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + void connectToolbar(); + void setTransitionActive(bool b); + void openEasingCurveEditor(); + + TransitionEditorToolBar *m_toolbar = nullptr; + + QGraphicsView *m_rulerView = nullptr; + + QGraphicsView *m_graphicsView = nullptr; + + QScrollBar *m_scrollbar = nullptr; + + QLabel *m_statusBar = nullptr; + + TransitionEditorView *m_transitionEditorView = nullptr; + + TransitionEditorGraphicsScene *m_graphicsScene; + + QPushButton *m_addButton = nullptr; + + QWidget *m_onboardingContainer = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp new file mode 100644 index 00000000000..239817197ba --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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 "transitionform.h" +#include "timelineform.h" +#include "ui_transitionform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace QmlDesigner { + +TransitionForm::TransitionForm(QWidget *parent) + : QWidget(parent) + , ui(new Ui::TransitionForm) +{ + ui->setupUi(this); + + connect(ui->idLineEdit, &QLineEdit::editingFinished, [this]() { + QTC_ASSERT(m_transition.isValid(), return ); + + static QString lastString; + + const QString newId = ui->idLineEdit->text(); + + if (newId == lastString) + return; + + lastString = newId; + + if (newId == m_transition.id()) + return; + + bool error = false; + + if (!ModelNode::isValidId(newId)) { + Core::AsynchronousMessageBox::warning(tr("Invalid Id"), + tr("%1 is an invalid id.").arg(newId)); + error = true; + } else if (m_transition.view()->hasId(newId)) { + Core::AsynchronousMessageBox::warning(tr("Invalid Id"), + tr("%1 already exists.").arg(newId)); + error = true; + } else { + m_transition.setIdWithRefactoring(newId); + } + + if (error) { + lastString.clear(); + ui->idLineEdit->setText(m_transition.id()); + } + }); + + connect(ui->listWidgetTo, &QListWidget::itemChanged, this, [this]() { + QTC_ASSERT(m_transition.isValid(), return ); + const QmlItemNode root(m_transition.view()->rootModelNode()); + QTC_ASSERT(root.isValid(), return ); + const int stateCount = root.states().names().count(); + + QStringList stateNames; + + for (const QListWidgetItem *item : ui->listWidgetTo->findItems("*", Qt::MatchWildcard)) { + if (item->checkState() == Qt::Checked) + stateNames.append(item->text()); + } + + QString toValue; + if (stateCount == stateNames.count()) + toValue = "*"; + else + toValue = stateNames.join(","); + + m_transition.view()->executeInTransaction("TransitionForm::Set To", [this, toValue]() { + m_transition.variantProperty("to").setValue(toValue); + }); + }); + + connect(ui->listWidgetFrom, &QListWidget::itemChanged, this, [this]() { + QTC_ASSERT(m_transition.isValid(), return ); + const QmlItemNode root(m_transition.view()->rootModelNode()); + QTC_ASSERT(root.isValid(), return ); + const int stateCount = root.states().names().count(); + + QStringList stateNames; + + for (const QListWidgetItem *item : ui->listWidgetFrom->findItems("*", Qt::MatchWildcard)) { + if (item->checkState() == Qt::Checked) + stateNames.append(item->text()); + } + + QString fromValue; + if (stateCount == stateNames.count()) + fromValue = "*"; + else + fromValue = stateNames.join(","); + + m_transition.view()->executeInTransaction("TransitionForm::Set To", [this, fromValue]() { + m_transition.variantProperty("from").setValue(fromValue); + }); + }); +} + +TransitionForm::~TransitionForm() +{ + delete ui; +} + +void TransitionForm::setTransition(const ModelNode &transition) +{ + m_transition = transition; + + if (m_transition.isValid()) { + ui->idLineEdit->setText(m_transition.displayName()); + } + setupStatesLists(); +} + +ModelNode TransitionForm::transition() const +{ + return m_transition; +} + +void TransitionForm::setupStatesLists() +{ + bool bTo = ui->listWidgetTo->blockSignals(true); + bool bFrom = ui->listWidgetFrom->blockSignals(true); + QAbstractItemModel *modelTo = ui->listWidgetTo->model(); + modelTo->removeRows(0, modelTo->rowCount()); + + QAbstractItemModel *modelFrom = ui->listWidgetFrom->model(); + modelFrom->removeRows(0, modelFrom->rowCount()); + + bool starFrom = true; + bool starTo = true; + + QStringList fromList; + QStringList toList; + + if (m_transition.hasVariantProperty("from") + && m_transition.variantProperty("from").value().toString().trimmed() != "*") { + starFrom = false; + fromList = m_transition.variantProperty("from").value().toString().split(","); + } + + if (m_transition.hasVariantProperty("to") + && m_transition.variantProperty("to").value().toString().trimmed() != "*") { + starTo = false; + toList = m_transition.variantProperty("to").value().toString().split(","); + } + + if (m_transition.isValid()) { + const QmlItemNode root(m_transition.view()->rootModelNode()); + if (root.isValid()) { + const QmlModelStateGroup states = root.states(); + for (const QString &stateName : states.names()) { + auto itemTo = new QListWidgetItem(stateName, ui->listWidgetTo); + ui->listWidgetTo->addItem(itemTo); + itemTo->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + if (starTo || toList.contains(stateName)) + itemTo->setCheckState(Qt::Checked); + else + itemTo->setCheckState(Qt::Unchecked); + + auto itemFrom = new QListWidgetItem(stateName, ui->listWidgetFrom); + ui->listWidgetFrom->addItem(itemFrom); + itemFrom->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + if (starFrom || fromList.contains(stateName)) + itemFrom->setCheckState(Qt::Checked); + else + itemFrom->setCheckState(Qt::Unchecked); + } + } + } + ui->listWidgetTo->blockSignals(bTo); + ui->listWidgetFrom->blockSignals(bFrom); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.h b/src/plugins/qmldesigner/components/transitioneditor/transitionform.h new file mode 100644 index 00000000000..99995577693 --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QSpinBox) + +namespace QmlDesigner { + +namespace Ui { +class TransitionForm; +} + +class TransitionForm : public QWidget +{ + Q_OBJECT + +public: + explicit TransitionForm(QWidget *parent); + ~TransitionForm() override; + void setTransition(const ModelNode &transition); + ModelNode transition() const; + +private: + void setupStatesLists(); + + Ui::TransitionForm *ui; + ModelNode m_transition; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.ui b/src/plugins/qmldesigner/components/transitioneditor/transitionform.ui new file mode 100644 index 00000000000..560c2065afd --- /dev/null +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.ui @@ -0,0 +1,87 @@ + + + QmlDesigner::TransitionForm + + + + 0 + 0 + 641 + 170 + + + + + + + + 160 + 0 + + + + + 75 + true + + + + Timeline Settings + + + + + + + + + + Transition ID: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 49 + 20 + + + + + + + + + + + + + + + + + + From + + + + + + + To + + + + + + + + diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index e7d3e0a23d5..f6fc86dacd0 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -243,6 +244,10 @@ bool QmlDesignerPlugin::delayedInitialize() timelineView->registerActions(); } + auto transitionEditorView = new QmlDesigner::TransitionEditorView; + d->viewManager.registerViewTakingOwnership(transitionEditorView); + transitionEditorView->registerActions(); + d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::SourceTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::ColorTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index 0095aa8f10b..bc0da990504 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -32,6 +32,7 @@ include(components/curveeditor/curveeditor.pri) include(components/bindingeditor/bindingeditor.pri) include(components/annotationeditor/annotationeditor.pri) include(components/richtexteditor/richtexteditor.pri) +include(components/transitioneditor/transitioneditor.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index c3db90d2e0f..b1129a9bf33 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -843,6 +843,26 @@ Project { "timelineeditor/timelineview.h", "timelineeditor/timelinewidget.cpp", "timelineeditor/timelinewidget.h", + "transitioneditor/transitioneditorview.cpp", + "transitioneditor/transitioneditorview.h", + "transitioneditor/transitioneditorwidget.cpp", + "transitioneditor/transitioneditorwidget.h", + "transitioneditor/transitioneditortoolbar.cpp", + "transitioneditor/transitioneditortoolbar.h", + "transitioneditor/transitioneditorgraphicsscene.cpp", + "transitioneditor/transitioneditorgraphicsscene.h", + "transitioneditor/transitioneditorgraphicslayout.cpp", + "transitioneditor/transitioneditorgraphicslayout.h", + "transitioneditor/transitioneditorsectionitem.cpp", + "transitioneditor/transitioneditorsectionitem.h", + "transitioneditor/transitioneditorpropertyitem.cpp", + "transitioneditor/transitioneditorpropertyitem.h", + "transitioneditor/transitioneditorsettingsdialog.cpp", + "transitioneditor/transitioneditorsettingsdialog.h", + "transitioneditor/transitioneditorsettingsdialog.ui" + "transitioneditor/transitionform.cpp", + "transitioneditor/transitionform.h", + "transitioneditor/transitioneditor.qrc" ] } From 000281fed770b5af96d093724fb1be1347bf5eed Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 19 Jun 2020 19:43:18 +0200 Subject: [PATCH 40/46] AssetExport: Refactor to accommodate asset generation Change-Id: I57b33dc06819b3f0d1269eae10bd1131c5cb911d Reviewed-by: Thomas Hartmann --- .../assetexporterplugin/assetexporter.cpp | 6 ++-- .../assetexporterplugin/componentexporter.cpp | 32 +++++++++++++------ .../assetexporterplugin/componentexporter.h | 23 +++++++------ .../parsers/modelitemnodeparser.cpp | 6 ++-- .../parsers/modelitemnodeparser.h | 3 +- .../parsers/modelnodeparser.h | 7 ++-- .../parsers/textnodeparser.cpp | 5 +-- .../parsers/textnodeparser.h | 4 ++- 8 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 27c83ff5d68..e85ac11cabe 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -88,9 +88,9 @@ bool AssetExporter::isBusy() const void AssetExporter::exportComponent(const ModelNode &rootNode) { qCDebug(loggerInfo) << "Exporting component" << rootNode.id(); - ComponentExporter exporter(rootNode); - QJsonObject json = exporter.exportComponent(); - m_components.append(json); + Component exporter(*this, rootNode); + exporter.exportComponent(); + m_components.append(exporter.json()); } void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp index a02af72f2ea..819fa3d328f 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp @@ -51,20 +51,31 @@ static void populateLineage(const QmlDesigner::ModelNode &node, QByteArrayList & namespace QmlDesigner { -std::vector> ComponentExporter::m_readers; -ComponentExporter::ComponentExporter(const ModelNode &rootNode): +std::vector> Component::m_readers; +Component::Component(AssetExporter &exporter, const ModelNode &rootNode): + m_exporter(exporter), m_rootNode(rootNode) { } -QJsonObject ComponentExporter::exportComponent() const +QJsonObject Component::json() const { - QTC_ASSERT(m_rootNode.isValid(), return {}); - return nodeToJson(m_rootNode); + return m_json; } -ModelNodeParser *ComponentExporter::createNodeParser(const ModelNode &node) const +AssetExporter &Component::exporter() +{ + return m_exporter; +} + +void Component::exportComponent() +{ + QTC_ASSERT(m_rootNode.isValid(), return); + m_json = nodeToJson(m_rootNode); +} + +ModelNodeParser *Component::createNodeParser(const ModelNode &node) const { QByteArrayList lineage; populateLineage(node, lineage); @@ -80,18 +91,19 @@ ModelNodeParser *ComponentExporter::createNodeParser(const ModelNode &node) cons } } } - if (!reader) { + + if (!reader) qCDebug(loggerInfo()) << "No parser for node" << node; - } + return reader.release(); } -QJsonObject ComponentExporter::nodeToJson(const ModelNode &node) const +QJsonObject Component::nodeToJson(const ModelNode &node) { QJsonObject jsonObject; std::unique_ptr parser(createNodeParser(node)); if (parser) - jsonObject = parser->json(); + jsonObject = parser->json(*this); QJsonArray children; for (const ModelNode &childnode : node.directSubModelNodes()) diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h index 3565b190a7b..3668f372bfb 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h @@ -24,11 +24,9 @@ ****************************************************************************/ #pragma once -#include -#include +#include #include -#include #include #include "utils/qtcassert.h" @@ -38,9 +36,9 @@ class QJsonArray; QT_END_NAMESPACE namespace QmlDesigner { -class Model; +class AssetExporter; class ModelNode; -class ComponentExporter; +class Component; class ModelNodeParser; namespace Internal { @@ -50,7 +48,7 @@ public: virtual ~NodeParserCreatorBase() {} protected: virtual ModelNodeParser *instance(const QByteArrayList &, const ModelNode &) const = 0; - friend class QmlDesigner::ComponentExporter; + friend class QmlDesigner::Component; }; template @@ -67,12 +65,15 @@ protected: }; } //Internal -class ComponentExporter +class Component { public: - ComponentExporter(const ModelNode &rootNode); + Component(AssetExporter& exporter, const ModelNode &rootNode); - QJsonObject exportComponent() const; + void exportComponent(); + QJsonObject json() const; + + AssetExporter &exporter(); template static void addNodeParser() { @@ -81,10 +82,12 @@ public: } private: ModelNodeParser* createNodeParser(const ModelNode &node) const; - QJsonObject nodeToJson(const ModelNode &node) const; + QJsonObject nodeToJson(const ModelNode &node); private: + AssetExporter& m_exporter; const ModelNode &m_rootNode; + QJsonObject m_json; static std::vector> m_readers; }; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp index 08366c4119b..355983f2215 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp @@ -30,7 +30,8 @@ namespace QmlDesigner { using namespace Constants; -ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node) : +ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, + const ModelNode &node) : ModelNodeParser(lineage, node) { @@ -41,8 +42,9 @@ bool QmlDesigner::ItemNodeParser::isExportable() const return lineage().contains("QtQuick.Item"); } -QJsonObject QmlDesigner::ItemNodeParser::json() const +QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component) const { + Q_UNUSED(component); const QmlObjectNode &qmlObjectNode = objectNode(); QJsonObject jsonObject; jsonObject.insert(QmlIdTag, qmlObjectNode.id()); diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h index 332eada4101..503fb4c2e9b 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h @@ -28,6 +28,7 @@ namespace QmlDesigner { class ModelNode; +class Component; class ItemNodeParser : public ModelNodeParser { @@ -38,6 +39,6 @@ public: int priority() const override { return 100; } bool isExportable() const override; - QJsonObject json() const override; + QJsonObject json(Component &component) const override; }; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h index 6b0807d5371..4ca17746e8d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h @@ -30,6 +30,7 @@ #include namespace QmlDesigner { +class Component; class ModelNode; class ModelNodeParser @@ -41,14 +42,16 @@ public: virtual int priority() const = 0; virtual bool isExportable() const = 0; - virtual QJsonObject json() const = 0; + virtual QJsonObject json(Component& component) const = 0; const QByteArrayList& lineage() const { return m_lineage; } const QmlObjectNode& objectNode() const { return m_objectNode; } QVariant propertyValue(const PropertyName &name) const; -private: +protected: const ModelNode &m_node; + +private: QmlObjectNode m_objectNode; QByteArrayList m_lineage; }; diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp index 1f5a9e70280..12b73c4506a 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp @@ -61,9 +61,10 @@ bool TextNodeParser::isExportable() const return lineage().contains("QtQuick.Text"); } -QJsonObject TextNodeParser::json() const +QJsonObject TextNodeParser::json(Component &component) const { - QJsonObject jsonObject = ItemNodeParser::json(); + Q_UNUSED(component); + QJsonObject jsonObject = ItemNodeParser::json(component); QJsonObject textDetails; textDetails.insert(TextContentTag, propertyValue("text").toString()); diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h index 29c5fbf3c56..c05d5c8f883 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h @@ -27,6 +27,8 @@ #include "modelitemnodeparser.h" namespace QmlDesigner { +class Component; + class TextNodeParser : public ItemNodeParser { public: @@ -35,7 +37,7 @@ public: bool isExportable() const override; int priority() const override { return 200; } - QJsonObject json() const override; + QJsonObject json(Component &component) const override; }; } From 9daf5c130da6edb0f03b9f7f61c34c368d4cca56 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Fri, 19 Jun 2020 10:00:47 +0200 Subject: [PATCH 41/46] AssetExport: Export assets from renderable nodes Task-number: QDS-1555 Change-Id: I3d5b60ee8214aeee054587f45045beea020d1f13 Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetexporterplugin/assetexporter.cpp | 162 +++++++++++++++++- .../assetexporterplugin/assetexporter.h | 7 + .../assetexporterplugin.cpp | 9 +- .../assetexporterplugin.pri | 2 + .../assetexporterplugin.qbs | 2 + .../assetexportpluginconstants.h | 2 +- .../parsers/assetnodeparser.cpp | 66 +++++++ .../parsers/assetnodeparser.h | 42 +++++ 9 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp create mode 100644 src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 94a4f449a76..12b58ac14f3 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -52,6 +52,7 @@ add_qtc_plugin(assetexporterplugin assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp + assetexporterplugin/parsers/assetnodeparser.h assetexporterplugin/parsers/assetnodeparser.cpp assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp assetexporterplugin/parsers/textnodeparser.h assetexporterplugin/parsers/textnodeparser.cpp diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index e85ac11cabe..49d763f6b42 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -26,15 +26,39 @@ #include "componentexporter.h" #include "exportnotification.h" +#include "qmlitemnode.h" +#include "qmlobjectnode.h" #include "utils/qtcassert.h" +#include "utils/runextensions.h" +#include "variantproperty.h" +#include +#include #include #include #include +#include + +#include +#include using namespace ProjectExplorer; - +using namespace std; namespace { +bool makeParentPath(const Utils::FilePath &path) +{ + QDir d; + return d.mkpath(path.toFileInfo().absolutePath()); +} + +QByteArray generateHash(const QString &token) { + static uint counter = 0; + std::mt19937 gen(std::random_device().operator()()); + std::uniform_int_distribution<> distribution(1, 99999); + QByteArray data = QString("%1%2%3").arg(token).arg(++counter).arg(distribution(gen)).toLatin1(); + return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex(); +} + Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg) Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg) Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg) @@ -42,6 +66,34 @@ Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", namespace QmlDesigner { +class AssetDumper +{ +public: + AssetDumper(); + ~AssetDumper(); + + void dumpAsset(const QPixmap &p, const Utils::FilePath &path); + + /* Keeps on dumping until all assets are dumped, then quits */ + void quitDumper(); + + /* Aborts dumping */ + void abortDumper(); + +private: + void addAsset(const QPixmap &p, const Utils::FilePath &path); + void doDumping(QFutureInterface &fi); + void savePixmap(const QPixmap &p, Utils::FilePath &path) const; + + QFuture m_dumpFuture; + QMutex m_queueMutex; + QWaitCondition m_queueCondition; + std::queue> m_assets; + std::atomic m_quitDumper; +}; + + + AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) : QObject(parent), m_currentState(*this), @@ -71,11 +123,13 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil m_exportPath = exportPath; m_currentState.change(ParsingState::Parsing); triggerLoadNextFile(); + m_assetDumper = make_unique(); } void AssetExporter::cancel() { // TODO Cancel export + m_assetDumper.reset(); } bool AssetExporter::isBusy() const @@ -85,6 +139,21 @@ bool AssetExporter::isBusy() const m_currentState == AssetExporter::ParsingState::WritingJson; } +Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node) +{ + // TODO: Use this hash as UUID and add to the node. + QByteArray hash; + do { + hash = generateHash(node.id()); + } while (m_usedHashes.contains(hash)); + m_usedHashes.insert(hash); + + Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png") + .arg(QString::fromLatin1(hash))); + m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); + return assetPath; +} + void AssetExporter::exportComponent(const ModelNode &rootNode) { qCDebug(loggerInfo) << "Exporting component" << rootNode.id(); @@ -149,6 +218,7 @@ void AssetExporter::writeMetadata() const Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata"); ExportNotification::addInfo(tr("Writing metadata to file %1."). arg(metadataPath.toUserOutput())); + makeParentPath(metadataPath); m_currentState.change(ParsingState::WritingJson); QJsonObject jsonRoot; // TODO: Write plugin info to root jsonRoot.insert("artboards", m_components); @@ -165,6 +235,7 @@ void AssetExporter::writeMetadata() const } notifyProgress(1.0); ExportNotification::addInfo(tr("Export finished.")); + m_assetDumper->quitDumper(); m_currentState.change(ParsingState::ExportingDone); } @@ -189,4 +260,93 @@ QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s) return os; } +AssetDumper::AssetDumper(): + m_quitDumper(false) +{ + m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this); +} + +AssetDumper::~AssetDumper() +{ + abortDumper(); +} + +void AssetDumper::dumpAsset(const QPixmap &p, const Utils::FilePath &path) +{ + addAsset(p, path); +} + +void AssetDumper::quitDumper() +{ + m_quitDumper = true; + m_queueCondition.wakeAll(); + if (!m_dumpFuture.isFinished()) + m_dumpFuture.waitForFinished(); +} + +void AssetDumper::abortDumper() +{ + if (!m_dumpFuture.isFinished()) { + m_dumpFuture.cancel(); + m_queueCondition.wakeAll(); + m_dumpFuture.waitForFinished(); + } +} + +void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path) +{ + QMutexLocker locker(&m_queueMutex); + qDebug() << "Save Asset:" << path; + m_assets.push({p, path}); +} + +void AssetDumper::doDumping(QFutureInterface &fi) +{ + auto haveAsset = [this] (std::pair *asset) { + QMutexLocker locker(&m_queueMutex); + if (m_assets.empty()) + return false; + *asset = m_assets.front(); + m_assets.pop(); + return true; + }; + + forever { + std::pair asset; + if (haveAsset(&asset)) { + if (fi.isCanceled()) + break; + savePixmap(asset.first, asset.second); + } else { + if (m_quitDumper) + break; + QMutexLocker locker(&m_queueMutex); + m_queueCondition.wait(&m_queueMutex); + } + + if (fi.isCanceled()) + break; + } + fi.reportFinished(); +} + +void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const +{ + if (p.isNull()) { + qCDebug(loggerWarn) << "Dumping null pixmap" << path; + return; + } + + if (!makeParentPath(path)) { + ExportNotification::addError(AssetExporter::tr("Error creating asset directory. %1") + .arg(path.fileName())); + return; + } + + if (!p.save(path.toString())) { + ExportNotification::addError(AssetExporter::tr("Error saving asset. %1") + .arg(path.fileName())); + } +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index 9c2212f5e23..a4d6df23c03 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -29,6 +29,7 @@ #include #include +#include #include @@ -41,6 +42,7 @@ class Project; } namespace QmlDesigner { +class AssetDumper; class AssetExporter : public QObject { @@ -68,6 +70,8 @@ public: void cancel(); bool isBusy() const; + Utils::FilePath exportAsset(const QmlObjectNode& node); + signals: void stateChanged(ParsingState); void exportProgressChanged(double) const; @@ -82,6 +86,7 @@ private: void loadNextFile(); void onQmlFileLoaded(); + private: mutable class State { public: @@ -96,6 +101,8 @@ private: Utils::FilePaths m_exportFiles; Utils::FilePath m_exportPath; QJsonArray m_components; + QSet m_usedHashes; + std::unique_ptr m_assetDumper; }; QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index fcd492cb0e3..e45e48ca0db 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -34,6 +34,7 @@ #include "parsers/modelitemnodeparser.h" #include "parsers/textnodeparser.h" +#include "parsers/assetnodeparser.h" #include "coreplugin/actionmanager/actionmanager.h" #include "coreplugin/actionmanager/actioncontainer.h" @@ -68,8 +69,9 @@ AssetExporterPlugin::AssetExporterPlugin() : viewManager.registerViewTakingOwnership(m_view); // Add parsers templates for factory instantiation. - ComponentExporter::addNodeParser(); - ComponentExporter::addNodeParser(); + Component::addNodeParser(); + Component::addNodeParser(); + Component::addNodeParser(); // Instantiate actions created by the plugin. addActions(); @@ -93,7 +95,8 @@ void AssetExporterPlugin::onExport() return; FilePathModel model(startupProject); - auto exportDir = startupProject->projectFilePath().parentDir(); + QString exportDirName = startupProject->displayName() + "_export"; + auto exportDir = startupProject->projectFilePath().parentDir().pathAppended(exportDirName); AssetExporter assetExporter(m_view, startupProject); AssetExportDialog assetExporterDialog(exportDir, assetExporter, model); assetExporterDialog.exec(); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri index b0e4f6392a5..713ab1184fb 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -15,6 +15,7 @@ HEADERS += \ componentexporter.h \ exportnotification.h \ filepathmodel.h \ + parsers/assetnodeparser.h \ parsers/modelitemnodeparser.h \ parsers/modelnodeparser.h \ parsers/textnodeparser.h @@ -27,6 +28,7 @@ SOURCES += \ componentexporter.cpp \ exportnotification.cpp \ filepathmodel.cpp \ + parsers/assetnodeparser.cpp \ parsers/modelitemnodeparser.cpp \ parsers/modelnodeparser.cpp \ parsers/textnodeparser.cpp diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs index e8ce2537361..e847525324d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -47,6 +47,8 @@ QtcProduct { "exportnotification.h", "filepathmodel.cpp", "filepathmodel.h", + "parsers/assetnodeparser.cpp", + "parsers/assetnodeparser.h", "parsers/modelitemnodeparser.cpp", "parsers/modelitemnodeparser.h", "parsers/modelnodeparser.cpp", diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 169d99ebfbe..2e9ba7a56d2 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -57,7 +57,7 @@ const char ImportsTag[] = "extraImports"; const char UuidTag[] = "uuid"; const char ClipTag[] = "clip"; const char AssetDataTag[] = "assetData"; -const char AssetPath[] = "assetPath"; +const char AssetPathTag[] = "assetPath"; const char AssetBoundsTag[] = "assetBounds"; const char OpacityTag[] = "opacity"; diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp new file mode 100644 index 00000000000..159eccec460 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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 "assetnodeparser.h" +#include "assetexportpluginconstants.h" +#include "assetexporter.h" + +#include "qmlitemnode.h" +#include "componentexporter.h" + +#include "utils/fileutils.h" + +#include + +namespace QmlDesigner { +using namespace Constants; +AssetNodeParser::AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node) : + ItemNodeParser(lineage, node) +{ + +} + +bool AssetNodeParser::isExportable() const +{ + auto hasType = [this](const QByteArray &type) { + return lineage().contains(type); + }; + return hasType("QtQuick.Image") || hasType("QtQuick.Rectangle"); +} + +QJsonObject AssetNodeParser::json(Component &component) const +{ + QJsonObject jsonObject = ItemNodeParser::json(component); + + QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap(); + Utils::FilePath assetPath = component.exporter().exportAsset(objectNode()); + + QJsonObject assetData; + assetData.insert(AssetPathTag, assetPath.toString()); + jsonObject.insert(AssetDataTag, assetData); + return jsonObject; +} +} + diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h new file mode 100644 index 00000000000..be764b17ec2 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** 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 "modelitemnodeparser.h" + +namespace QmlDesigner { +class Component; + +class AssetNodeParser : public ItemNodeParser +{ +public: + AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node); + ~AssetNodeParser() override = default; + + bool isExportable() const override; + int priority() const override { return 200; } + QJsonObject json(Component &component) const override; +}; +} From 3fde0a19ecb6be05eadb54cdc30d540d32372bc5 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Thu, 25 Jun 2020 17:17:05 +0200 Subject: [PATCH 42/46] AssetExport: Add generated UUID to qml This will enable the merge when importing back from Photoshop Task-number: QDS-1555 Change-Id: I411ad65af1a33a80fcea80206aef93e2d1afa357 Reviewed-by: Thomas Hartmann --- .../assetexporterplugin/assetexporter.cpp | 30 +++++++++++++++---- .../assetexporterplugin/assetexporter.h | 2 ++ .../assetexporterplugin/assetexporterview.cpp | 11 ++++++- .../assetexporterplugin/assetexporterview.h | 1 + .../assetexportpluginconstants.h | 1 + 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 49d763f6b42..26e2d2c455b 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -25,7 +25,9 @@ #include "assetexporter.h" #include "componentexporter.h" #include "exportnotification.h" +#include "assetexportpluginconstants.h" +#include "rewriterview.h" #include "qmlitemnode.h" #include "qmlobjectnode.h" #include "utils/qtcassert.h" @@ -142,12 +144,7 @@ bool AssetExporter::isBusy() const Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node) { // TODO: Use this hash as UUID and add to the node. - QByteArray hash; - do { - hash = generateHash(node.id()); - } while (m_usedHashes.contains(hash)); - m_usedHashes.insert(hash); - + QByteArray hash = addNodeUUID(node.modelNode()); Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png") .arg(QString::fromLatin1(hash))); m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); @@ -189,9 +186,30 @@ void AssetExporter::onQmlFileLoaded() QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return); qCDebug(loggerInfo) << "Qml file load done" << m_view->model()->fileUrl(); exportComponent(m_view->rootModelNode()); + QString error; + if (!m_view->saveQmlFile(&error)) { + ExportNotification::addError(tr("Error saving QML file. %1") + .arg(error.isEmpty()? tr("Unknown") : error)); + } triggerLoadNextFile(); } +QByteArray AssetExporter::addNodeUUID(ModelNode node) +{ + QByteArray uuid = node.auxiliaryData(Constants::UuidTag).toByteArray(); + qDebug() << node.id() << "UUID" << uuid; + if (uuid.isEmpty()) { + // Assign a new hash. + do { + uuid = generateHash(node.id()); + } while (m_usedHashes.contains(uuid)); + m_usedHashes.insert(uuid); + node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid)); + node.model()->rewriterView()->writeAuxiliaryData(); + } + return uuid; +} + void AssetExporter::triggerLoadNextFile() { QTimer::singleShot(0, this, &AssetExporter::loadNextFile); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index a4d6df23c03..2aa238fb327 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -87,6 +87,8 @@ private: void onQmlFileLoaded(); + QByteArray addNodeUUID(ModelNode node); + private: mutable class State { public: diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp index 1e47632c8ef..fbb4173249f 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp @@ -62,7 +62,7 @@ bool AssetExporterView::loadQmlFile(const Utils::FilePath &path, uint timeoutSec setState(LoadState::Busy); m_retryCount = std::max(MinRetry, static_cast((timeoutSecs * 1000) / RetryIntervalMs)); - Core::EditorManager::openEditor(path.toString(), Core::Id(), + m_currentEditor = Core::EditorManager::openEditor(path.toString(), Core::Id(), Core::EditorManager::DoNotMakeVisible); Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN); Core::ModeManager::setFocusToCurrentMode(); @@ -70,6 +70,15 @@ bool AssetExporterView::loadQmlFile(const Utils::FilePath &path, uint timeoutSec return true; } +bool AssetExporterView::saveQmlFile(QString *error) const +{ + if (!m_currentEditor) { + qCDebug(loggerWarn) << "Saving QML file failed. No editor."; + return false; + } + return m_currentEditor->document()->save(error); +} + void AssetExporterView::modelAttached(Model *model) { if (model->rewriterView() && model->rewriterView()->inErrorState()) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h index 2fb45d7865c..46c2c77071a 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h @@ -54,6 +54,7 @@ public: AssetExporterView(QObject *parent = nullptr); bool loadQmlFile(const Utils::FilePath &path, uint timeoutSecs = 10); + bool saveQmlFile(QString *error) const; void modelAttached(Model *model) override; void instanceInformationsChanged(const QMultiHash &informationChangeHash) override; diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 2e9ba7a56d2..1937c7126eb 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -30,6 +30,7 @@ namespace Constants { const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml"; const char TASK_CATEGORY_ASSET_EXPORT[] = "AssetExporter.Export"; +const char UuidAuxTag[] = "uuid"; //*************************************************************************** // Metadata tags From 5a06305ffe054287f55cba4c5d860ea73fccf684 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 26 Jun 2020 14:34:07 +0200 Subject: [PATCH 43/46] qmldesigner: enable Transion in QmlUi files Change-Id: I2e35e8a2dc7b946297c70fd775bd3e3366295271 Reviewed-by: Thomas Hartmann --- src/libs/qmljs/qmljscheck.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index b90236e026b..c18a4174fbe 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -621,7 +621,6 @@ class UnsupportedTypesByQmlUi : public QStringList public: UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect", "Component", - "Transition", "Drawer"}) { append(UnsupportedTypesByVisualDesigner()); From 2949504a9de89cfe4106cb9de7d846e486e3ad65 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 26 Jun 2020 16:25:46 +0300 Subject: [PATCH 44/46] QmlDesigner: Fix draggable priorities in 3D edit gizmos Upped default priority of all directional draggables a bit to make them preferable to free rotation ball. Change-Id: If934cb723e8c1a8ca9a114d71b19cbfc196e2682 Fixes: QDS-2286 Reviewed-by: Mahmoud Badri --- .../qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml | 2 ++ share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml | 2 +- share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml index 1401333627c..e88b9c4c51c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml @@ -104,6 +104,7 @@ Model { grabsMouse: targetNode active: rootModel.active dragHelper: rootModel.dragHelper + priority: 5 onPressed: rootModel.handlePressed(mouseAreaYZ, planePos, screenPos) onDragged: rootModel.handleDragged(mouseAreaYZ, planePos, screenPos) @@ -121,6 +122,7 @@ Model { grabsMouse: targetNode active: rootModel.active dragHelper: rootModel.dragHelper + priority: 5 onPressed: rootModel.handlePressed(mouseAreaXZ, planePos, screenPos) onDragged: rootModel.handleDragged(mouseAreaXZ, planePos, screenPos) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml index adb6f23a483..493e6cc3c25 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml @@ -161,7 +161,7 @@ Node { color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) : Qt.rgba(0.5, 0.5, 0.5, 1) rotation: view3D.camera.rotation - priority: 1 + priority: 10 targetNode: moveGizmo.targetNode view3D: moveGizmo.view3D diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml index ccfc45b6211..3b4918e272c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml @@ -185,7 +185,7 @@ Node { height: 120 rotation: view3D.camera.rotation grabsMouse: scaleGizmo.targetNode - priority: 1 + priority: 10 active: scaleGizmo.visible dragHelper: scaleGizmo.dragHelper From 2caedc7134af558cabf9a23ed70bf1922662919d Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Fri, 26 Jun 2020 17:43:29 +0200 Subject: [PATCH 45/46] CMake/Doc: fix small typo in link Change-Id: I1dcefbe25bff1faedf77175770ee7e2521101924 Reviewed-by: Leena Miettinen --- doc/qtcreator/src/cmake/creator-projects-cmake.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index 0a76cddc407..fa055e6975b 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -59,7 +59,7 @@ \section1 Adding CMake Tools - \QC requires CMake's {https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} + \QC requires CMake's \l{https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} {file-based API}. Please make sure to use CMake version 3.14, or later. To specify paths to CMake executables: From c90d1c61ce1feee157dc25bc3fb49f993aca5c43 Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Fri, 26 Jun 2020 14:37:48 +0300 Subject: [PATCH 46/46] Android: recommend AdoptOpenJDK in settings UI and docs Show JDK downlaod button on Linux and point it to OpenJDK becuse it's the most used on Linux. Otherwise, recommend AdoptOpenJDK because it works on all platforms and easy to install as well. Change-Id: I94fd61262fe759b96db59a9e2abfbd063f6795f0 Reviewed-by: Alessandro Portale Reviewed-by: Leena Miettinen --- doc/qtcreator/src/android/androiddev.qdoc | 8 ++++---- .../src/external-resources/external-resources.qdoc | 8 ++++++++ src/plugins/android/androidsettingswidget.cpp | 7 ++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc index 950fe6e9aae..6af2926a080 100644 --- a/doc/qtcreator/src/android/androiddev.qdoc +++ b/doc/qtcreator/src/android/androiddev.qdoc @@ -57,11 +57,11 @@ To use \QC to develop Qt applications for Android, you need the following: \list - \li \l{https://www.oracle.com/java/technologies/javase-jdk8-downloads.html} - {Java SE Development Kit (JDK)} version 6 up to 8. - You can also use \l{http://openjdk.java.net/}{OpenJDK} on Linux. + \li \l{AdoptOpenJDK} for all platforms. You can also use \l{OpenJDK} + on Linux. - \note Android SDK Tools have issues with JDK versions later than 8. + \note Android SDK Tools versions <= 26.x have issues with JDK versions + later than 8. It is recommended to use the latest Command-line SDK Tools. \li \l{http://www.gradle.org}{Gradle} for building application packages (APK) and app bundles (AAB) for Android devices. Gradle is delivered diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index ba1517344f1..9114cfe3ee9 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -45,3 +45,11 @@ \externalpage https://doc.qt.io/QtForMCUs/qtul-getting-started-windows.html \title Getting Started on Windows */ +/*! + \externalpage https://adoptopenjdk.net/ + \title AdoptOpenJDK +*/ +/*! + \externalpage http://openjdk.java.net + \title OpenJDK +*/ diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 404d3e1952c..b5aa0455897 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -454,8 +454,6 @@ AndroidSettingsWidget::AndroidSettingsWidget() m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - m_ui.downloadOpenJDKToolButton->setVisible(!HostOsInfo::isLinuxHost()); - const QIcon downloadIcon = Icons::ONLINE.icon(); m_ui.downloadSDKToolButton->setIcon(downloadIcon); m_ui.downloadNDKToolButton->setIcon(downloadIcon); @@ -696,7 +694,10 @@ void AndroidSettingsWidget::openNDKDownloadUrl() void AndroidSettingsWidget::openOpenJDKDownloadUrl() { - QDesktopServices::openUrl(QUrl::fromUserInput("https://www.oracle.com/java/technologies/javase-jdk8-downloads.html")); + if (HostOsInfo::isLinuxHost()) + QDesktopServices::openUrl(QUrl::fromUserInput("https://openjdk.java.net/install/")); + else + QDesktopServices::openUrl(QUrl::fromUserInput("https://adoptopenjdk.net/")); } void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)