From 7e363e4168414b6d77d88e830ac757abcbcecaee Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 13 Feb 2020 18:50:35 +0100 Subject: [PATCH 1/4] Qdb: De-Q_OBJECT-ify RunConfiguration Change-Id: Ib632cb135915561682f68c0c37e0c3fefe92c0bb Reviewed-by: Christian Stenger --- src/plugins/boot2qt/qdbrunconfiguration.cpp | 47 +++++++++++++++------ src/plugins/boot2qt/qdbrunconfiguration.h | 20 --------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/plugins/boot2qt/qdbrunconfiguration.cpp b/src/plugins/boot2qt/qdbrunconfiguration.cpp index 97aa4a01e57..dbeb9e9e156 100644 --- a/src/plugins/boot2qt/qdbrunconfiguration.cpp +++ b/src/plugins/boot2qt/qdbrunconfiguration.cpp @@ -44,27 +44,46 @@ namespace Internal { // FullCommandLineAspect -FullCommandLineAspect::FullCommandLineAspect(RunConfiguration *rc) +class FullCommandLineAspect : public BaseStringAspect { - setLabelText(QdbRunConfiguration::tr("Full command line:")); + Q_DECLARE_TR_FUNCTIONS(Qdb::Internal::QdbRunConfiguration); - auto exeAspect = rc->aspect(); - auto argumentsAspect = rc->aspect(); +public: + explicit FullCommandLineAspect(RunConfiguration *rc) + { + setLabelText(tr("Full command line:")); - auto updateCommandLine = [this, rc, exeAspect, argumentsAspect] { - const QString usedExecutable = exeAspect->executable().toString(); - const QString args = argumentsAspect->arguments(rc->macroExpander()); - setValue(QString(Constants::AppcontrollerFilepath) - + ' ' + usedExecutable + ' ' + args); - }; + auto exeAspect = rc->aspect(); + auto argumentsAspect = rc->aspect(); + + auto updateCommandLine = [this, rc, exeAspect, argumentsAspect] { + const QString usedExecutable = exeAspect->executable().toString(); + const QString args = argumentsAspect->arguments(rc->macroExpander()); + setValue(QString(Constants::AppcontrollerFilepath) + + ' ' + usedExecutable + ' ' + args); + }; + + connect(argumentsAspect, &ArgumentsAspect::argumentsChanged, this, updateCommandLine); + connect(exeAspect, &ExecutableAspect::changed, this, updateCommandLine); + updateCommandLine(); + } +}; - connect(argumentsAspect, &ArgumentsAspect::argumentsChanged, this, updateCommandLine); - connect(exeAspect, &ExecutableAspect::changed, this, updateCommandLine); - updateCommandLine(); -} // QdbRunConfiguration +class QdbRunConfiguration : public RunConfiguration +{ + Q_DECLARE_TR_FUNCTIONS(Qdb::Internal::QdbRunConfiguration); + +public: + QdbRunConfiguration(Target *target, Core::Id id); + +private: + Tasks checkForIssues() const override; + QString defaultDisplayName() const; +}; + QdbRunConfiguration::QdbRunConfiguration(Target *target, Core::Id id) : RunConfiguration(target, id) { diff --git a/src/plugins/boot2qt/qdbrunconfiguration.h b/src/plugins/boot2qt/qdbrunconfiguration.h index 0804d269eda..a9a81c3c1d5 100644 --- a/src/plugins/boot2qt/qdbrunconfiguration.h +++ b/src/plugins/boot2qt/qdbrunconfiguration.h @@ -30,26 +30,6 @@ namespace Qdb { namespace Internal { -class FullCommandLineAspect : public ProjectExplorer::BaseStringAspect -{ - Q_OBJECT - -public: - explicit FullCommandLineAspect(ProjectExplorer::RunConfiguration *rc); -}; - -class QdbRunConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - QdbRunConfiguration(ProjectExplorer::Target *target, Core::Id id); - -private: - ProjectExplorer::Tasks checkForIssues() const override; - QString defaultDisplayName() const; -}; - class QdbRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory { public: From 9caf355e11eb649474ffd61e9b4a3c4f8aa00f29 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 11 Feb 2020 11:09:04 +0100 Subject: [PATCH 2/4] Nim: Move RunConfigurations to .cpp, drop Q_OBJECT Also remove NimbleRunConfigurationFactory::availableCreators reimplementation that was the same as the base. Change-Id: Ied86c08e77a5bb70d8a16cdeddebd99c4ea5120c Reviewed-by: Christian Stenger --- .../nim/project/nimblerunconfiguration.cpp | 79 +++++++++++-------- .../nim/project/nimblerunconfiguration.h | 25 +----- .../nim/project/nimrunconfiguration.cpp | 48 ++++++----- src/plugins/nim/project/nimrunconfiguration.h | 10 +-- 4 files changed, 78 insertions(+), 84 deletions(-) diff --git a/src/plugins/nim/project/nimblerunconfiguration.cpp b/src/plugins/nim/project/nimblerunconfiguration.cpp index c7f166182e6..ebb77a037db 100644 --- a/src/plugins/nim/project/nimblerunconfiguration.cpp +++ b/src/plugins/nim/project/nimblerunconfiguration.cpp @@ -37,29 +37,38 @@ #include -using namespace Nim; using namespace ProjectExplorer; -NimbleRunConfiguration::NimbleRunConfiguration(ProjectExplorer::Target *target, Core::Id id) - : RunConfiguration(target, id) +namespace Nim { + +// NimbleRunConfiguration + +class NimbleRunConfiguration : public RunConfiguration { - addAspect(target); - addAspect(); - addAspect(); - addAspect(); - addAspect(); + Q_DECLARE_TR_FUNCTIONS(Nim::NimbleRunConfiguration) - setUpdater([this] { - BuildTargetInfo bti = buildTargetInfo(); - setDisplayName(bti.displayName); - setDefaultDisplayName(bti.displayName); - aspect()->setExecutable(bti.targetFilePath); - aspect()->setDefaultWorkingDirectory(bti.workingDirectory); - }); +public: + NimbleRunConfiguration(Target *target, Core::Id id) + : RunConfiguration(target, id) + { + addAspect(target); + addAspect(); + addAspect(); + addAspect(); + addAspect(); - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - update(); -} + setUpdater([this] { + BuildTargetInfo bti = buildTargetInfo(); + setDisplayName(bti.displayName); + setDefaultDisplayName(bti.displayName); + aspect()->setExecutable(bti.targetFilePath); + aspect()->setDefaultWorkingDirectory(bti.workingDirectory); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + update(); + } +}; NimbleRunConfigurationFactory::NimbleRunConfigurationFactory() : RunConfigurationFactory() @@ -69,22 +78,26 @@ NimbleRunConfigurationFactory::NimbleRunConfigurationFactory() addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); } -QList NimbleRunConfigurationFactory::availableCreators(Target *parent) const -{ - return RunConfigurationFactory::availableCreators(parent); -} -NimbleTestConfiguration::NimbleTestConfiguration(Target *target, Core::Id id) - : RunConfiguration(target, id) -{ - addAspect()->setExecutable(Utils::FilePath::fromString(QStandardPaths::findExecutable("nimble"))); - addAspect()->setArguments("test"); - addAspect()->setDefaultWorkingDirectory(project()->projectDirectory()); - addAspect(); +// NimbleTestConfiguration - setDisplayName(tr("Nimble Test")); - setDefaultDisplayName(tr("Nimble Test")); -} +class NimbleTestConfiguration : public RunConfiguration +{ + Q_DECLARE_TR_FUNCTIONS(Nim::NimbleTestConfiguration) + +public: + NimbleTestConfiguration(ProjectExplorer::Target *target, Core::Id id) + : RunConfiguration(target, id) + { + addAspect()->setExecutable(Utils::FilePath::fromString(QStandardPaths::findExecutable("nimble"))); + addAspect()->setArguments("test"); + addAspect()->setDefaultWorkingDirectory(project()->projectDirectory()); + addAspect(); + + setDisplayName(tr("Nimble Test")); + setDefaultDisplayName(tr("Nimble Test")); + } +}; NimbleTestConfigurationFactory::NimbleTestConfigurationFactory() : FixedRunConfigurationFactory(QString()) @@ -92,3 +105,5 @@ NimbleTestConfigurationFactory::NimbleTestConfigurationFactory() registerRunConfiguration("Nim.NimbleTestConfiguration"); addSupportedProjectType(Constants::C_NIMBLEPROJECT_ID); } + +} // Nim diff --git a/src/plugins/nim/project/nimblerunconfiguration.h b/src/plugins/nim/project/nimblerunconfiguration.h index df300bc2839..bbc64489cf9 100644 --- a/src/plugins/nim/project/nimblerunconfiguration.h +++ b/src/plugins/nim/project/nimblerunconfiguration.h @@ -29,35 +29,16 @@ namespace Nim { -class NimbleRunConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - NimbleRunConfiguration(ProjectExplorer::Target *target, Core::Id id); -}; - -class NimbleRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory +class NimbleRunConfigurationFactory final : public ProjectExplorer::RunConfigurationFactory { public: NimbleRunConfigurationFactory(); - -protected: - QList availableCreators(ProjectExplorer::Target *parent) const override; }; -class NimbleTestConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - NimbleTestConfiguration(ProjectExplorer::Target *target, Core::Id id); -}; - -class NimbleTestConfigurationFactory : public ProjectExplorer::FixedRunConfigurationFactory +class NimbleTestConfigurationFactory final : public ProjectExplorer::FixedRunConfigurationFactory { public: NimbleTestConfigurationFactory(); }; -} +} // Nim diff --git a/src/plugins/nim/project/nimrunconfiguration.cpp b/src/plugins/nim/project/nimrunconfiguration.cpp index e803dd3a372..808d36e101a 100644 --- a/src/plugins/nim/project/nimrunconfiguration.cpp +++ b/src/plugins/nim/project/nimrunconfiguration.cpp @@ -41,31 +41,37 @@ using namespace Utils; namespace Nim { -NimRunConfiguration::NimRunConfiguration(Target *target, Core::Id id) - : RunConfiguration(target, id) +class NimRunConfiguration final : public RunConfiguration { - addAspect(target); - addAspect(); - addAspect(); - addAspect(); - addAspect(); + Q_DECLARE_TR_FUNCTIONS(Nim::NimRunConfiguration) - setDisplayName(tr("Current Build Target")); - setDefaultDisplayName(tr("Current Build Target")); +public: + NimRunConfiguration(Target *target, Core::Id id) + : RunConfiguration(target, id) + { + addAspect(target); + addAspect(); + addAspect(); + addAspect(); + addAspect(); - setUpdater([this] { - auto buildConfiguration = qobject_cast(activeBuildConfiguration()); - QTC_ASSERT(buildConfiguration, return); - const QFileInfo outFileInfo = buildConfiguration->outFilePath().toFileInfo(); - aspect()->setExecutable(FilePath::fromString(outFileInfo.absoluteFilePath())); - const QString workingDirectory = outFileInfo.absoluteDir().absolutePath(); - aspect()->setDefaultWorkingDirectory(FilePath::fromString(workingDirectory)); - }); + setDisplayName(tr("Current Build Target")); + setDefaultDisplayName(tr("Current Build Target")); - // Connect target signals - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - update(); -} + setUpdater([this] { + auto buildConfiguration = qobject_cast(activeBuildConfiguration()); + QTC_ASSERT(buildConfiguration, return); + const QFileInfo outFileInfo = buildConfiguration->outFilePath().toFileInfo(); + aspect()->setExecutable(FilePath::fromString(outFileInfo.absoluteFilePath())); + const QString workingDirectory = outFileInfo.absoluteDir().absolutePath(); + aspect()->setDefaultWorkingDirectory(FilePath::fromString(workingDirectory)); + }); + + // Connect target signals + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + update(); + } +}; // NimRunConfigurationFactory diff --git a/src/plugins/nim/project/nimrunconfiguration.h b/src/plugins/nim/project/nimrunconfiguration.h index df3968c44c6..2206bee49a9 100644 --- a/src/plugins/nim/project/nimrunconfiguration.h +++ b/src/plugins/nim/project/nimrunconfiguration.h @@ -29,15 +29,7 @@ namespace Nim { -class NimRunConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - NimRunConfiguration(ProjectExplorer::Target *target, Core::Id id); -}; - -class NimRunConfigurationFactory : public ProjectExplorer::FixedRunConfigurationFactory +class NimRunConfigurationFactory final : public ProjectExplorer::FixedRunConfigurationFactory { public: NimRunConfigurationFactory(); From c81d672bb0b2ac101f2c864b3933ec532cc750a6 Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Fri, 17 Jan 2020 15:58:12 +0100 Subject: [PATCH 3/4] QmlDesigner: QDS Annotations - Extended Auxiliary data support for Annotations - Extended Model Node interface - New Tool created - Form Editor & Property Editor visualization Task: QDS-39 Change-Id: Idf15f9b295cb2b977fd5557acffbd40757f13a8e Reviewed-by: Thomas Hartmann Reviewed-by: Tim Jenssen --- .../QtQuick/ItemPane.qml | 114 ++++- src/plugins/qmldesigner/CMakeLists.txt | 10 + .../annotationeditor/annotation.cpp | 307 ++++++++++++ .../components/annotationeditor/annotation.h | 122 +++++ .../annotationeditor/annotationcommenttab.cpp | 95 ++++ .../annotationeditor/annotationcommenttab.h | 66 +++ .../annotationeditor/annotationcommenttab.ui | 71 +++ .../annotationeditor/annotationeditor.cpp | 190 +++++++ .../annotationeditor/annotationeditor.h | 89 ++++ .../annotationeditor/annotationeditor.pri | 14 + .../annotationeditordialog.cpp | 248 ++++++++++ .../annotationeditor/annotationeditordialog.h | 79 +++ .../annotationeditordialog.ui | 131 +++++ .../annotationeditor/annotationtool.cpp | 258 ++++++++++ .../annotationeditor/annotationtool.h | 93 ++++ .../components/formeditor/annotationsIcon.png | Bin 0 -> 449 bytes .../formeditor/annotationsIconActive.png | Bin 0 -> 3750 bytes .../components/formeditor/formeditor.pri | 6 +- .../components/formeditor/formeditor.qrc | 2 + .../formeditor/formeditorannotationicon.cpp | 463 ++++++++++++++++++ .../formeditor/formeditorannotationicon.h | 100 ++++ .../components/formeditor/formeditorscene.cpp | 17 +- .../components/formeditor/formeditorscene.h | 4 + .../components/formeditor/formeditorview.cpp | 5 + .../formeditor/selectionindicator.cpp | 49 +- .../formeditor/selectionindicator.h | 6 + .../propertyeditorqmlbackend.cpp | 4 + .../quick2propertyeditorview.cpp | 2 + .../designercore/include/modelnode.h | 19 + .../designercore/model/modelnode.cpp | 95 ++++ .../designercore/model/rewriterview.cpp | 8 + src/plugins/qmldesigner/qmldesignerplugin.cpp | 2 + src/plugins/qmldesigner/qmldesignerplugin.pro | 1 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 14 + 34 files changed, 2673 insertions(+), 11 deletions(-) create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotation.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotation.h create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditor.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditor.h create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationtool.cpp create mode 100644 src/plugins/qmldesigner/components/annotationeditor/annotationtool.h create mode 100644 src/plugins/qmldesigner/components/formeditor/annotationsIcon.png create mode 100644 src/plugins/qmldesigner/components/formeditor/annotationsIconActive.png create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.h diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml index 96e98868bdb..bbc2444ac12 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml @@ -107,8 +107,8 @@ Rectangle { } Item { - Layout.preferredWidth: 16 - Layout.preferredHeight: 16 + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 } } @@ -134,8 +134,10 @@ Rectangle { Image { visible: !modelNodeBackend.multiSelection - Layout.preferredWidth: 16 - Layout.preferredHeight: 16 + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter source: hasAliasExport ? "image://icons/alias-export-checked" : "image://icons/alias-export-unchecked" ToolTipArea { enabled: !modelNodeBackend.multiSelection @@ -145,6 +147,110 @@ Rectangle { } } } + + Label { + text: qsTr("Custom id") + } + + SecondColumnLayout { + enabled: !modelNodeBackend.multiSelection + visible: enabled + spacing: 2 + + LineEdit { + id: annotationEdit + enabled: annotationEditor.hasAuxData + visible: enabled + + backendValue: backendValues.customId__AUX + placeholderText: qsTr("customId") + text: backendValue.value + Layout.fillWidth: true + Layout.preferredWidth: 240 + width: 240 + showTranslateCheckBox: false + showExtendedFunctionButton: false + + onHoveredChanged: annotationEditor.checkAux() + } + + StudioControls.AbstractButton { + id: editAnnotationButton + enabled: annotationEditor.hasAuxData + visible: enabled + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + width: 22 + + buttonIcon: StudioTheme.Constants.edit + + onClicked: annotationEditor.showWidget() + + onHoveredChanged: annotationEditor.checkAux() + } + + StudioControls.AbstractButton { + id: removeAnnotationButton + enabled: annotationEditor.hasAuxData + visible: enabled + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + width: 22 + + buttonIcon: StudioTheme.Constants.closeCross + + onClicked: annotationEditor.removeFullAnnotation() + + onHoveredChanged: annotationEditor.checkAux() + } + + StudioControls.AbstractButton { + id: addAnnotationButton + enabled: !annotationEditor.hasAuxData + visible: enabled + + buttonIcon: qsTr("Add Annotation") + iconFont: StudioTheme.Constants.font + Layout.fillWidth: true + Layout.preferredWidth: 240 + + onClicked: annotationEditor.showWidget() + + onHoveredChanged: annotationEditor.checkAux() + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + enabled: !annotationEditor.hasAuxData + visible: enabled + } + + AnnotationEditor { + id: annotationEditor + + modelNodeBackendProperty: modelNodeBackend + + property bool hasAuxData: (annotationEditor.hasAnnotation || annotationEditor.hasCustomId) + + onModelNodeBackendChanged: checkAux() + onCustomIdChanged: checkAux() + onAnnotationChanged: checkAux() + + function checkAux() { + hasAuxData = (annotationEditor.hasAnnotation || annotationEditor.hasCustomId) + annotationEdit.update() + } + + onAccepted: { + hideWidget() + } + + onCanceled: { + hideWidget() + } + } + } } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 4eb99638c74..ca2605877e2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -208,6 +208,7 @@ extend_qtc_plugin(QmlDesigner controlelement.cpp controlelement.h dragtool.cpp dragtool.h formeditor.qrc + formeditorannotationicon.cpp formeditorannotationicon.h formeditorgraphicsview.cpp formeditorgraphicsview.h formeditoritem.cpp formeditoritem.h formeditorscene.cpp formeditorscene.h @@ -538,6 +539,15 @@ extend_qtc_plugin(QmlDesigner SOURCES colortool.cpp colortool.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/annotationeditor + SOURCES annotation.cpp annotation.h + annotationcommenttab.cpp annotationcommenttab.h annotationcommenttab.ui + annotationeditordialog.cpp annotationeditordialog.h annotationeditordialog.ui + annotationeditor.cpp annotationeditor.h + annotationtool.cpp annotationtool.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/connectioneditor SOURCES diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotation.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotation.cpp new file mode 100644 index 00000000000..b5419b8d956 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotation.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** 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 "annotation.h" + +#include + +namespace QmlDesigner { + +static const QString s_sep = " //;;// "; //separator + +Comment::Comment() + : m_title(QString()) + , m_author(QString()) + , m_text(QString()) + , m_timestamp(0) +{} + +Comment::Comment(const QString &title, const QString &author, const QString &text, qint64 timestamp) + : m_title(title) + , m_author(author) + , m_text(text) + , m_timestamp(timestamp) +{} + +QString Comment::title() const +{ + return m_title; +} + +void Comment::setTitle(const QString &title) +{ + m_title = title; +} + +QString Comment::author() const +{ + return m_author; +} + +void Comment::setAuthor(const QString &author) +{ + m_author = author; +} + +QString Comment::text() const +{ + return m_text; +} + +void Comment::setText(const QString &text) +{ + m_text = text; +} + +QString Comment::timestampStr() const +{ + return QDateTime::fromSecsSinceEpoch(m_timestamp).toString(); +} + +QString Comment::timestampStr(const QString &format) const +{ + return QDateTime::fromSecsSinceEpoch(m_timestamp).toString(format); +} + +qint64 Comment::timestamp() const +{ + return m_timestamp; +} + +void Comment::setTimestamp(qint64 timestamp) +{ + m_timestamp = timestamp; +} + +void Comment::updateTimestamp() +{ + m_timestamp = QDateTime::currentSecsSinceEpoch(); +} + +bool Comment::sameContent(const Comment &comment) const +{ + return sameContent(*this, comment); +} + +bool Comment::sameContent(const Comment &a, const Comment &b) +{ + return ((a.title() == b.title()) + && (a.author() == b.author()) + && (a.text() == b.text())); +} + +bool Comment::operator==(const Comment &comment) const +{ + return (sameContent(comment) && (m_timestamp == comment.timestamp())); +} + +bool Comment::isEmpty() +{ + return sameContent(Comment()); +} + +QString Comment::toQString() const +{ + QStringList result; + + result.push_back(m_title); + result.push_back(m_author); + result.push_back(m_text); + result.push_back(QString::number(m_timestamp)); + + return result.join(s_sep); +} + +QDebug &operator<<(QDebug &stream, const Comment &comment) +{ + stream << "\"title: " << comment.m_title << "\" "; + stream << "\"author: " << comment.m_author << "\" "; + stream << "\"text: " << comment.m_text << "\" "; + stream << "\"timestamp: " << comment.m_timestamp << "\" "; + stream << "\"date/time: " << QDateTime::fromSecsSinceEpoch(comment.m_timestamp).toString() << "\" "; + + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const Comment &comment) +{ + stream << comment.m_title; + stream << comment.m_author; + stream << comment.m_text; + stream << comment.m_timestamp; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, Comment &comment) +{ + stream >> comment.m_title; + stream >> comment.m_author; + stream >> comment.m_text; + stream >> comment.m_timestamp; + + return stream; +} + + +//Annotation + +Annotation::Annotation() + : m_comments() +{ + +} + +QVector Annotation::comments() const +{ + return m_comments; +} + + +bool Annotation::hasComments() const +{ + return !m_comments.isEmpty(); +} + +void Annotation::setComments(const QVector &comments) +{ + m_comments = comments; +} + +void Annotation::removeComments() +{ + m_comments.clear(); +} + +int Annotation::commentsSize() const +{ + return m_comments.size(); +} + +Comment Annotation::comment(int n) const +{ + if (m_comments.size() > n) + return m_comments.at(n); + else + return Comment(); +} + +void Annotation::addComment(const Comment &comment) +{ + m_comments.push_back(comment); +} + +bool Annotation::updateComment(const Comment &comment, int n) +{ + bool result = false; + + if ((m_comments.size() > n) && (n > 0)) { + m_comments[n] = comment; + result = true; + } + + return result; +} + +bool Annotation::removeComment(int n) +{ + bool result = false; + + if (m_comments.size() > n) { + m_comments.remove(n); + result = true; + } + + return result; +} + +QString Annotation::toQString() const +{ + QStringList result; + + result.push_back(QString::number(m_comments.size())); + + for (const Comment &com : m_comments) + result.push_back(com.toQString()); + + return result.join(s_sep); +} + +void Annotation::fromQString(const QString &str) +{ + QStringList strl (str.split(s_sep, QString::SplitBehavior::KeepEmptyParts)); + removeComments(); + + const int intro = 1; + const int comSize = 4; + + if (!strl.isEmpty()) { + + if (strl.size() >= intro) { + + int size = strl.at(0).toInt(); + + if (size > 0) { + if (strl.size() == (size*comSize) + intro) + { + for (int i = 0; i < size; i++) + { + const int offset = intro + (i * comSize); + Comment com; + com.setTitle(strl.at(offset + 0)); + com.setAuthor(strl.at(offset + 1)); + com.setText(strl.at(offset + 2)); + com.setTimestamp(strl.at(offset + 3).toLongLong()); + + m_comments.push_back(com); + } + } + } + } + } +} + +QDebug &operator<<(QDebug &stream, const Annotation &annotation) +{ + stream << "\"Annotation: " << annotation.m_comments << "\" "; + + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const Annotation &annotation) +{ + stream << annotation.m_comments; + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, Annotation &annotation) +{ + stream >> annotation.m_comments; + + return stream; +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotation.h b/src/plugins/qmldesigner/components/annotationeditor/annotation.h new file mode 100644 index 00000000000..95fcf81fa21 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotation.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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 "nodeinstanceglobal.h" + +namespace QmlDesigner { + +static const PropertyName customIdProperty = {("customId")}; +static const PropertyName annotationProperty = {("annotation")}; + +class Comment +{ +public: + Comment(); + Comment(const QString &title, const QString &author = QString(), const QString &text = QString(), qint64 timestamp = 0); + + ~Comment() = default; + + QString title() const; + void setTitle(const QString &title); + + QString text() const; + void setText(const QString &text); + + QString author() const; + void setAuthor(const QString &author); + + QString timestampStr() const; + QString timestampStr(const QString &format) const; + qint64 timestamp() const; + void setTimestamp(qint64 timestamp); + void updateTimestamp(); + + bool sameContent(const Comment &comment) const; //everything is similar besides timestamp + static bool sameContent(const Comment &a, const Comment &b); + bool operator==(const Comment &comment) const; //everything is similar. + + bool isEmpty(); + + QString toQString() const; + + friend QDebug &operator<<(QDebug &stream, const Comment &comment); + + friend QDataStream &operator<<(QDataStream &stream, const Comment &comment); + friend QDataStream &operator>>(QDataStream &stream, Comment &comment); + +private: + QString m_title; + QString m_author; + QString m_text; + qint64 m_timestamp; +}; + +class Annotation +{ +public: + Annotation(); + ~Annotation() = default; + + QVector comments() const; + bool hasComments() const; + void setComments(const QVector &comments); + void removeComments(); + int commentsSize() const; + + Comment comment(int n) const; + void addComment(const Comment &comment); + bool updateComment(const Comment &comment, int n); + bool removeComment(int n); + + QString toQString() const; + void fromQString(const QString &str); + + friend QDebug &operator<<(QDebug &stream, const Annotation &annotation); + + friend QDataStream &operator<<(QDataStream &stream, const Annotation &annotation); + friend QDataStream &operator>>(QDataStream &stream, Annotation &annotation); + +private: + QVector m_comments; +}; + +QDebug &operator<<(QDebug &stream, const Comment &comment); +QDebug &operator<<(QDebug &stream, const Annotation &annotation); + +QDataStream &operator<<(QDataStream &stream, const Comment &comment); +QDataStream &operator>>(QDataStream &stream, Comment &comment); +QDataStream &operator<<(QDataStream &stream, const Annotation &annotation); +QDataStream &operator>>(QDataStream &stream, Annotation &annotation); + +} + +Q_DECLARE_METATYPE(QmlDesigner::Comment); +Q_DECLARE_METATYPE(QmlDesigner::Annotation); diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp new file mode 100644 index 00000000000..bd7aed67d02 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "annotationcommenttab.h" +#include "ui_annotationcommenttab.h" + +namespace QmlDesigner { + +AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) : + QWidget(parent), + ui(new Ui::AnnotationCommentTab) +{ + ui->setupUi(this); + + connect(ui->titleEdit, &QLineEdit::textEdited, + this, &AnnotationCommentTab::commentTitleChanged); +} + +AnnotationCommentTab::~AnnotationCommentTab() +{ + delete ui; +} + +Comment AnnotationCommentTab::currentComment() const +{ + Comment result; + + result.setTitle(ui->titleEdit->text().trimmed()); + result.setAuthor(ui->authorEdit->text().trimmed()); + result.setText(ui->textEdit->toPlainText().trimmed()); + + if (m_comment.sameContent(result)) + result.setTimestamp(m_comment.timestamp()); + else + result.updateTimestamp(); + + return result; +} + +Comment AnnotationCommentTab::originalComment() const +{ + return m_comment; +} + +void AnnotationCommentTab::setComment(const Comment &comment) +{ + m_comment = comment; + resetUI(); +} + +void AnnotationCommentTab::resetUI() +{ + ui->titleEdit->setText(m_comment.title()); + ui->authorEdit->setText(m_comment.author()); + ui->textEdit->setText(m_comment.text()); + + if (m_comment.timestamp() > 0) + ui->timeLabel->setText(m_comment.timestampStr()); + else + ui->timeLabel->setText(""); +} + +void AnnotationCommentTab::resetComment() +{ + m_comment = currentComment(); +} + +void AnnotationCommentTab::commentTitleChanged(const QString &text) +{ + emit titleChanged(text, this); +} + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.h new file mode 100644 index 00000000000..cc8d4c3d769 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.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 "annotation.h" + +namespace QmlDesigner { + +namespace Ui { +class AnnotationCommentTab; +} + +class AnnotationCommentTab : public QWidget +{ + Q_OBJECT + +public: + explicit AnnotationCommentTab(QWidget *parent = nullptr); + ~AnnotationCommentTab(); + + Comment currentComment() const; + + Comment originalComment() const; + void setComment(const Comment &comment); + + void resetUI(); + void resetComment(); + +signals: + void titleChanged(const QString &text, QWidget *widget); + +private slots: + void commentTitleChanged(const QString &text); + +private: + Ui::AnnotationCommentTab *ui; + + Comment m_comment; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui new file mode 100644 index 00000000000..f6bf277eb7c --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui @@ -0,0 +1,71 @@ + + + QmlDesigner::AnnotationCommentTab + + + + 0 + 0 + 537 + 382 + + + + Form + + + + + + + + Title + + + + + + + + + + Text + + + + + + + true + + + + + + + Author + + + + + + + + + + + + + + + + + + + titleEdit + authorEdit + textEdit + + + + diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.cpp new file mode 100644 index 00000000000..46a98a07479 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** 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 "annotationeditor.h" + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "qmlmodelnodeproxy.h" +#include + +#include +#include +#include +#include + +namespace QmlDesigner { + +AnnotationEditor::AnnotationEditor(QObject *) +{ +} + +AnnotationEditor::~AnnotationEditor() +{ + hideWidget(); +} + +void AnnotationEditor::registerDeclarativeType() +{ + qmlRegisterType("HelperWidgets", 2, 0, "AnnotationEditor"); +} + +void AnnotationEditor::showWidget() +{ + m_dialog = new AnnotationEditorDialog(Core::ICore::dialogParent(), + modelNode().validId(), + modelNode().customId(), + modelNode().annotation()); + + QObject::connect(m_dialog, &AnnotationEditorDialog::accepted, + this, &AnnotationEditor::acceptedClicked); + QObject::connect(m_dialog, &AnnotationEditorDialog::rejected, + this, &AnnotationEditor::cancelClicked); + + m_dialog->setAttribute(Qt::WA_DeleteOnClose); + + m_dialog->open(); +} + +void AnnotationEditor::showWidget(int x, int y) +{ + showWidget(); + m_dialog->move(x, y); +} + +void AnnotationEditor::hideWidget() +{ + if (m_dialog) + m_dialog->close(); + m_dialog = nullptr; +} + +void AnnotationEditor::setModelNode(const ModelNode &modelNode) +{ + m_modelNodeBackend = {}; + m_modelNode = modelNode; +} + +ModelNode AnnotationEditor::modelNode() const +{ + return m_modelNode; +} + +void AnnotationEditor::setModelNodeBackend(const QVariant &modelNodeBackend) +{ + if (!modelNodeBackend.isNull() && modelNodeBackend.isValid()) { + m_modelNodeBackend = modelNodeBackend; + + const auto modelNodeBackendObject = modelNodeBackend.value(); + const auto backendObjectCasted = + qobject_cast(modelNodeBackendObject); + + if (backendObjectCasted) + m_modelNode = backendObjectCasted->qmlObjectNode().modelNode(); + + emit modelNodeBackendChanged(); + } +} + +QVariant AnnotationEditor::modelNodeBackend() const +{ + return m_modelNodeBackend; +} + +bool AnnotationEditor::hasCustomId() const +{ + if (m_modelNode.isValid()) + return m_modelNode.hasCustomId(); + return false; +} + +bool AnnotationEditor::hasAnnotation() const +{ + if (m_modelNode.isValid()) + return m_modelNode.hasAnnotation(); + return false; +} + +void AnnotationEditor::removeFullAnnotation() +{ + if (!m_modelNode.isValid()) + return; + + QString dialogTitle = tr("Annotation"); + if (!m_modelNode.customId().isNull()) { + dialogTitle = m_modelNode.customId(); + } + QMessageBox *deleteDialog = new QMessageBox(Core::ICore::dialogParent()); + deleteDialog->setWindowTitle(dialogTitle); + deleteDialog->setText(tr("Delete this annotation?")); + deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + deleteDialog->setDefaultButton(QMessageBox::Yes); + + int result = deleteDialog->exec(); + if (deleteDialog) deleteDialog->deleteLater(); + + if (result == QMessageBox::Yes) { + m_modelNode.removeCustomId(); + m_modelNode.removeAnnotation(); + } + + emit customIdChanged(); + emit annotationChanged(); +} + +void AnnotationEditor::acceptedClicked() +{ + if (m_dialog) { + QString customId = m_dialog->customId(); + Annotation annotation = m_dialog->annotation(); + + m_modelNode.setCustomId(customId); + + if (annotation.comments().isEmpty()) + m_modelNode.removeAnnotation(); + else + m_modelNode.setAnnotation(annotation); + } + + hideWidget(); + + emit accepted(); + + emit customIdChanged(); + emit annotationChanged(); +} + +void AnnotationEditor::cancelClicked() +{ + hideWidget(); + + emit canceled(); + + emit customIdChanged(); + emit annotationChanged(); +} + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.h b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.h new file mode 100644 index 00000000000..85e8c8432cc --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.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 +#include +#include + +#include "annotationeditordialog.h" +#include "annotation.h" + +#include "modelnode.h" + +namespace QmlDesigner { + +class AnnotationEditor : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) + Q_PROPERTY(bool hasCustomId READ hasCustomId NOTIFY customIdChanged) + Q_PROPERTY(bool hasAnnotation READ hasAnnotation NOTIFY annotationChanged) + +public: + explicit AnnotationEditor(QObject *parent = nullptr); + ~AnnotationEditor(); + + static void registerDeclarativeType(); + + Q_INVOKABLE void showWidget(); + Q_INVOKABLE void showWidget(int x, int y); + Q_INVOKABLE void hideWidget(); + + void setModelNode(const ModelNode &modelNode); + ModelNode modelNode() const; + + void setModelNodeBackend(const QVariant &modelNodeBackend); + QVariant modelNodeBackend() const; + + Q_INVOKABLE bool hasCustomId() const; + Q_INVOKABLE bool hasAnnotation() const; + + Q_INVOKABLE void removeFullAnnotation(); + +signals: + void accepted(); + void canceled(); + void modelNodeBackendChanged(); + + void customIdChanged(); + void annotationChanged(); + +private slots: + void acceptedClicked(); + void cancelClicked(); + +private: + QPointer m_dialog; + + ModelNode m_modelNode; + QVariant m_modelNodeBackend; +}; + +} //namespace QmlDesigner + +QML_DECLARE_TYPE(QmlDesigner::AnnotationEditor) diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri new file mode 100644 index 00000000000..3cc6c6f856b --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditor.pri @@ -0,0 +1,14 @@ +HEADERS += $$PWD/annotation.h +HEADERS += $$PWD/annotationtool.h +HEADERS += $$PWD/annotationcommenttab.h +HEADERS += $$PWD/annotationeditordialog.h +HEADERS += $$PWD/annotationeditor.h + +SOURCES += $$PWD/annotation.cpp +SOURCES += $$PWD/annotationtool.cpp +SOURCES += $$PWD/annotationcommenttab.cpp +SOURCES += $$PWD/annotationeditordialog.cpp +SOURCES += $$PWD/annotationeditor.cpp + +FORMS += $$PWD/annotationcommenttab.ui +FORMS += $$PWD/annotationeditordialog.ui diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp new file mode 100644 index 00000000000..ec99af6498f --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** 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 "annotationeditordialog.h" +#include "ui_annotationeditordialog.h" +#include "annotation.h" +#include "annotationcommenttab.h" + +#include "ui_annotationcommenttab.h" + +#include +#include +#include +#include + + +#include "timelineicons.h" //replace timeline icons with our own? + +namespace QmlDesigner { + +AnnotationEditorDialog::AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation) + : QDialog(parent) + , ui(new Ui::AnnotationEditorDialog) + , m_customId(customId) + , m_annotation(annotation) +{ + ui->setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowFlag(Qt::Tool, true); + setWindowTitle(titleString); + + connect(this, &QDialog::accepted, this, &AnnotationEditorDialog::acceptedClicked); + + connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AnnotationEditorDialog::tabChanged); + + auto *commentCornerWidget = new QToolBar; + + auto *commentAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(), tr("Add Comment")); //timeline icons? + auto *commentRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(), + tr("Remove Comment")); //timeline icons? + + connect(commentAddAction, &QAction::triggered, this, [this]() { + addComment(Comment()); + }); + + connect(commentRemoveAction, &QAction::triggered, this, [this]() { + + if (ui->tabWidget->count() == 0) //it is not even supposed to happen but lets be sure + return; + + int currentIndex = ui->tabWidget->currentIndex(); + QString currentTitle = ui->tabWidget->tabText(currentIndex); + + QMessageBox *deleteDialog = new QMessageBox(this); + deleteDialog->setWindowTitle(currentTitle); + deleteDialog->setText(tr("Delete this comment?")); + deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + deleteDialog->setDefaultButton(QMessageBox::Yes); + + int result = deleteDialog->exec(); + + if (result == QMessageBox::Yes) { + removeComment(currentIndex); + } + + if (ui->tabWidget->count() == 0) //lets be sure that tabWidget is never empty + addComment(Comment()); + }); + + commentCornerWidget->addAction(commentAddAction); + commentCornerWidget->addAction(commentRemoveAction); + + ui->tabWidget->setCornerWidget(commentCornerWidget, Qt::TopRightCorner); + ui->targetIdEdit->setText(targetId); + + fillFields(); +} + +AnnotationEditorDialog::~AnnotationEditorDialog() +{ + delete ui; +} + +void AnnotationEditorDialog::setAnnotation(const Annotation &annotation) +{ + m_annotation = annotation; + fillFields(); +} + +Annotation AnnotationEditorDialog::annotation() const +{ + return m_annotation; +} + +void AnnotationEditorDialog::setCustomId(const QString &customId) +{ + m_customId = customId; + ui->customIdEdit->setText(m_customId); +} + +QString AnnotationEditorDialog::customId() const +{ + return m_customId; +} + +void AnnotationEditorDialog::acceptedClicked() +{ + m_customId = ui->customIdEdit->text(); + + Annotation annotation; + + annotation.removeComments(); + + for (int i = 0; i < ui->tabWidget->count(); i++) { + AnnotationCommentTab* tab = reinterpret_cast(ui->tabWidget->widget(i)); + if (!tab) + continue; + + Comment comment = tab->currentComment(); + + if (!comment.isEmpty()) + annotation.addComment(comment); + } + + m_annotation = annotation; + + emit AnnotationEditorDialog::accepted(); +} + +void AnnotationEditorDialog::commentTitleChanged(const QString &text, QWidget *tab) +{ + int tabIndex = ui->tabWidget->indexOf(tab); + if (tabIndex >= 0) + ui->tabWidget->setTabText(tabIndex, text); + + if (text.isEmpty()) + ui->tabWidget->setTabText(tabIndex, + (defaultTabName + " " + QString::number(tabIndex+1))); +} + +void AnnotationEditorDialog::fillFields() +{ + ui->customIdEdit->setText(m_customId); + setupComments(); +} + +void AnnotationEditorDialog::setupComments() +{ + ui->tabWidget->setUpdatesEnabled(false); + + deleteAllTabs(); + + const QVector comments = m_annotation.comments(); + + if (comments.isEmpty()) + addComment(Comment()); + + for (const Comment &comment : comments) { + addCommentTab(comment); + } + + ui->tabWidget->setUpdatesEnabled(true); +} + +void AnnotationEditorDialog::addComment(const Comment &comment) +{ + m_annotation.addComment(comment); + addCommentTab(comment); +} + +void AnnotationEditorDialog::removeComment(int index) +{ + if ((m_annotation.commentsSize() > index) && (index >= 0)) { + m_annotation.removeComment(index); + removeCommentTab(index); + } +} + +void AnnotationEditorDialog::addCommentTab(const Comment &comment) +{ + auto commentTab = new AnnotationCommentTab(); + commentTab->setComment(comment); + int tabIndex = ui->tabWidget->addTab(commentTab, comment.title()); + + if (comment.title().isEmpty()) + ui->tabWidget->setTabText(tabIndex, + (defaultTabName + " " + QString::number(tabIndex+1))); + + connect(commentTab, &AnnotationCommentTab::titleChanged, + this, &AnnotationEditorDialog::commentTitleChanged); +} + +void AnnotationEditorDialog::removeCommentTab(int index) +{ + if ((ui->tabWidget->count() > index) && (index >= 0)) { + ui->tabWidget->removeTab(index); + } +} + +void AnnotationEditorDialog::deleteAllTabs() +{ + while (ui->tabWidget->count() > 0) { + QWidget *w = ui->tabWidget->widget(0); + ui->tabWidget->removeTab(0); + delete w; + } +} + +void AnnotationEditorDialog::tabChanged(int index) +{ + QWidget *w = ui->tabWidget->widget(index); + AnnotationCommentTab *tab = nullptr; + if (w) + tab = reinterpret_cast(w); + + if (tab) { + //this tab order resetting doesn't work + QWidget::setTabOrder(ui->targetIdEdit, ui->customIdEdit); + QWidget::setTabOrder(ui->customIdEdit, ui->tabWidget); + QWidget::setTabOrder(ui->tabWidget, tab); + QWidget::setTabOrder(tab, ui->buttonBox); + } +} + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h new file mode 100644 index 00000000000..1324115a724 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 "annotation.h" + +namespace QmlDesigner { + +namespace Ui { +class AnnotationEditorDialog; +} + +class AnnotationEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AnnotationEditorDialog(QWidget *parent, const QString &targetId, const QString &customId, const Annotation &annotation); + ~AnnotationEditorDialog(); + + void setAnnotation(const Annotation &annotation); + Annotation annotation() const; + + void setCustomId(const QString &customId); + QString customId() const; + +signals: + void accepted(); + +private slots: + void acceptedClicked(); + void tabChanged(int index); + void commentTitleChanged(const QString &text, QWidget *tab); + +private: + void fillFields(); + void setupComments(); + void addComment(const Comment &comment); + void removeComment(int index); + + void addCommentTab(const Comment &comment); + void removeCommentTab(int index); + void deleteAllTabs(); + +private: + const QString titleString = {tr("Annotation Editor")}; + const QString defaultTabName = {tr("Annotation")}; + Ui::AnnotationEditorDialog *ui; + + QString m_customId; + Annotation m_annotation; +}; + +} //namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui new file mode 100644 index 00000000000..8ca9b75136c --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationeditordialog.ui @@ -0,0 +1,131 @@ + + + QmlDesigner::AnnotationEditorDialog + + + + 0 + 0 + 700 + 487 + + + + Dialog + + + + + + + + + + Selected Item + + + + + + + false + + + true + + + + + + + + + + + Custom ID + + + + + + + + + + + + + + 0 + + + true + + + + Tab 1 + + + + + Tab 2 + + + + + + + + Qt::StrongFocus + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + targetIdEdit + customIdEdit + tabWidget + + + + + buttonBox + accepted() + QmlDesigner::AnnotationEditorDialog + accept() + + + 261 + 473 + + + 157 + 274 + + + + + buttonBox + rejected() + QmlDesigner::AnnotationEditorDialog + reject() + + + 329 + 473 + + + 286 + 274 + + + + + diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationtool.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationtool.cpp new file mode 100644 index 00000000000..522505dd9ca --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationtool.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** 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 "annotationtool.h" + +#include "formeditorscene.h" +#include "formeditorview.h" +#include "formeditorwidget.h" +#include "itemutilfunctions.h" +#include "formeditoritem.h" + +#include "nodemetainfo.h" +#include "qmlitemnode.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +class AnnotationToolAction : public AbstractAction +{ +public: + AnnotationToolAction() : AbstractAction(QCoreApplication::translate("AnnotationToolAction","Edit Annotation")) + { + } + + QByteArray category() const override + { + return QByteArray(); + } + + QByteArray menuId() const override + { + return "AnnotationTool"; + } + + int priority() const override + { + return CustomActionsPriority + 5; + } + + Type type() const override + { + return FormEditorAction; + } + +protected: + bool isVisible(const SelectionContext &selectionContext) const override + { + return selectionContext.singleNodeIsSelected(); + } + + bool isEnabled(const SelectionContext &selectionContext) const override + { + return isVisible(selectionContext); + } +}; + +AnnotationTool::AnnotationTool() +{ + auto annotationToolAction = new AnnotationToolAction; + QmlDesignerPlugin::instance()->designerActionManager().addDesignerAction(annotationToolAction); + connect(annotationToolAction->action(), &QAction::triggered, [=]() { + view()->changeCurrentToolTo(this); + }); +} + +AnnotationTool::~AnnotationTool() = default; + +void AnnotationTool::clear() +{ + if (m_annotationEditor) + m_annotationEditor->deleteLater(); + + AbstractFormEditorTool::clear(); +} + +void AnnotationTool::mousePressEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mousePressEvent(itemList, event); +} + +void AnnotationTool::mouseMoveEvent(const QList & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ +} + +void AnnotationTool::hoverMoveEvent(const QList & /*itemList*/, + QGraphicsSceneMouseEvent * /*event*/) +{ +} + +void AnnotationTool::keyPressEvent(QKeyEvent * /*keyEvent*/) +{ +} + +void AnnotationTool::keyReleaseEvent(QKeyEvent * /*keyEvent*/) +{ +} + +void AnnotationTool::dragLeaveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void AnnotationTool::dragMoveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent * /*event*/) +{ +} + +void AnnotationTool::mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mouseReleaseEvent(itemList, event); +} + + +void AnnotationTool::mouseDoubleClickEvent(const QList &itemList, QGraphicsSceneMouseEvent *event) +{ + AbstractFormEditorTool::mouseDoubleClickEvent(itemList, event); +} + +void AnnotationTool::itemsAboutToRemoved(const QList &removedItemList) +{ + if (m_annotationEditor.isNull()) + return; + + if (removedItemList.contains(m_formEditorItem)) + view()->changeToSelectionTool(); +} + +void AnnotationTool::selectedItemsChanged(const QList &itemList) +{ + if (!itemList.isEmpty()) { + m_formEditorItem = itemList.constFirst(); + + m_oldCustomId = m_formEditorItem->qmlItemNode().modelNode().customId(); + m_oldAnnotation = m_formEditorItem->qmlItemNode().modelNode().annotation(); + + if (m_annotationEditor.isNull()) { + m_annotationEditor = new AnnotationEditorDialog(view()->formEditorWidget()->parentWidget(), + m_formEditorItem->qmlItemNode().modelNode().displayName(), + m_oldCustomId, m_oldAnnotation); + + connect(m_annotationEditor, &AnnotationEditorDialog::accepted, this, &AnnotationTool::annotationDialogAccepted); + connect(m_annotationEditor, &QDialog::rejected, this, &AnnotationTool::annotationDialogRejected); +// connect(m_colorDialog.data(), &QColorDialog::currentColorChanged, this, &ColorTool::currentColorChanged); + + m_annotationEditor->exec(); + } + } else { + view()->changeToSelectionTool(); + } +} + +void AnnotationTool::instancesCompleted(const QList & /*itemList*/) +{ +} + +void AnnotationTool::instancesParentChanged(const QList & /*itemList*/) +{ +} + +void AnnotationTool::instancePropertyChange(const QList > & /*propertyList*/) +{ +} + +void AnnotationTool::formEditorItemsChanged(const QList & /*itemList*/) +{ +} + +int AnnotationTool::wantHandleItem(const ModelNode & /*modelNode*/) const +{ + return 10; +} + +QString AnnotationTool::name() const +{ + return tr("Annotation Tool"); +} + +void AnnotationTool::annotationDialogAccepted() +{ + if (m_annotationEditor) { + saveNewCustomId(m_annotationEditor->customId()); + saveNewAnnotation(m_annotationEditor->annotation()); + + m_annotationEditor->close(); + m_annotationEditor->deleteLater(); + } + + m_annotationEditor = nullptr; + + view()->changeToSelectionTool(); +} + +void AnnotationTool::saveNewCustomId(const QString &customId) +{ + if (m_formEditorItem) { + m_oldCustomId = customId; + m_formEditorItem->qmlItemNode().modelNode().setCustomId(customId); + } +} + +void AnnotationTool::saveNewAnnotation(const Annotation &annotation) +{ + if (m_formEditorItem) { + if (annotation.comments().isEmpty()) + m_formEditorItem->qmlItemNode().modelNode().removeAnnotation(); + else + m_formEditorItem->qmlItemNode().modelNode().setAnnotation(annotation); + + m_oldAnnotation = annotation; + } +} + +void AnnotationTool::annotationDialogRejected() +{ + if (m_annotationEditor) { + m_annotationEditor->close(); + m_annotationEditor->deleteLater(); + } + + m_annotationEditor = nullptr; + + view()->changeToSelectionTool(); +} + +} diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationtool.h b/src/plugins/qmldesigner/components/annotationeditor/annotationtool.h new file mode 100644 index 00000000000..0073286dd62 --- /dev/null +++ b/src/plugins/qmldesigner/components/annotationeditor/annotationtool.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 "annotation.h" +#include "annotationeditordialog.h" +#include "abstractcustomtool.h" +#include "selectionindicator.h" + +#include +#include +#include + +namespace QmlDesigner { + +class AnnotationTool : public QObject, public AbstractCustomTool +{ + Q_OBJECT +public: + AnnotationTool(); + ~AnnotationTool() override; + + void mousePressEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseDoubleClickEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + + void dragLeaveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + void dragMoveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent * event) override; + + void itemsAboutToRemoved(const QList &itemList) override; + + void selectedItemsChanged(const QList &itemList) override; //impl needed + + void instancesCompleted(const QList &itemList) override; + void instancesParentChanged(const QList &itemList) override; + void instancePropertyChange(const QList > &propertyList) override; + + void clear() override; + + void formEditorItemsChanged(const QList &itemList) override; + + int wantHandleItem(const ModelNode &modelNode) const override; + + QString name() const override; + +private: + void annotationDialogAccepted(); + void annotationDialogRejected(); + void saveNewCustomId(const QString &customId); + void saveNewAnnotation(const Annotation &annotation); + +private: + FormEditorItem *m_formEditorItem = nullptr; + QString m_oldCustomId; + Annotation m_oldAnnotation; + QPointer m_annotationEditor; +}; + +} diff --git a/src/plugins/qmldesigner/components/formeditor/annotationsIcon.png b/src/plugins/qmldesigner/components/formeditor/annotationsIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a18e863a4f2373d561fcd914ec82a040ef6e3d6b GIT binary patch literal 449 zcmeAS@N?(olHy`uVBq!ia0y~yV6b6eU@+idV_;zLwUgvzU|`@Z@Q5sCVBi)4Va7{$ z>;5n>Fcy2dIEG|2zMX04ci2FpHGc8CnVSqs6Vnxxh1_49aO-0*Z;+lMoM3jWfLY5; zzvP{hINPQx>KsO;0!_;n_Rc-B|G)nyk&_?Q-Ou}feDUSeV;KiN1-4v~hD?zTot|Vl zR;6e`_rnqH^MkU8`G=n}T)QYK6Llpq@zn-{ z(mCS&%aksd#@MVlywFuvY#ASS@bTUDa=G0+;{2})G6LIMR|+rLa6g>)_rfh7Oaxx8 z=l&QNnb-HwMB9(yg<)|)`kHvPFB6n1x7vJ7^$Mzq6tPN}OFLv1|Pf^Gcy9 zQ`~IUwPw3>H4|J<%gW^#ynPh#u=Qul{aumEZ21KDO;y?{xhg=o^iY=f-k6#GKX%?% z-M(0FOWpRx>cT#8r?a+&dHc>16=tYfQ|tM_jiH9=sYiyNzuLlh1_lNOPgg&ebxsLQ E04N>4CIA2c literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/annotationsIconActive.png b/src/plugins/qmldesigner/components/formeditor/annotationsIconActive.png new file mode 100644 index 0000000000000000000000000000000000000000..8367d46f2a28346d2c2bc23757e94e70ee1b48ef GIT binary patch literal 3750 zcmeAS@N?(olHy`uVBq!ia0y~yV6b6eU@+idV_;zLwUgvzVBlF-6%tVrlvu7%P?VpR znUkteQdy9ykXn(M#=uZ<>*}n?GG)t6F8?=n#WAHFnBM$x&b@=tb@A>kDnXtrjY5BZ zKH|kH>XKk!ykmBA>b3uW%V+=pAe_8bZCTLfCpkYmg^rrv3#tAa6Q2I~XKme~9rnfN zzx(cZAAMf;@8f+FuixEQXkTj+@Thjb{eAPNatE*PUi`axJ706(o66$ttIORF#(%&2 zcl*TOJH_Ou75`sdbnHs|x_-OAvfK%0T?{DIsc0W+M<#oJu_}{siPn;gravaKE z{x&W#{oegLru?4kH*S_~=QtlAG5bB+Phq$3wbSF0%LS_DOsuYcJ>OpA-`kV%8*f)% z+k1WME-_<|)MEWp_hRRio4j$5JeNE({9*i~T1)+hp^=XoHge9Md)>fq4*T1;i)OFe z`S#jQKJyt#Urvj!yT>noZm(3AxP4QQz}KhS^e-6ruJ~Y_pj&Wz|M{bH3!nCw$D}@a znekF~uI0QRPgCRW{yF)2@8Z2v^Ph|OtSA>h7`ODe$rQ`KzSvIj@u1A>n{(wiyuTh zTy9t_zv#WegP>2BXY!gi&JC#fYGrm_r2N*eC+lywh;?`s_#kIbV} z!{Sn}s;aw3?XQ88HvDy)$dhZ@%(-z&rgLNN$?ayBrFN=JpT;u#;1&<|f|u)@%Z$rB z3hK5S)pl3++A$aI<*`2%zu;PRj1` z`}^KMO}aUA;d%AMojn^)d)d5MdS=g|HHqIRx!Su=NHz(+-S_$Q%pJCY{)zQ(W}Vx+ zQ}yN5Z5yfzvo9?9zT>Qzy0-G^OPkDgs5yD?Gy)5Xy+vabXHnO^3v&y!usr!Go0No_;4{B zh*a))Hs5syM{di(W9#Nl=FnPcK2uvjmqTXpnQOCFy9qV!JtsBO@3fBN@@Tb*wbOj8 z4!AFUe0uM#rb*)F(i2nqq;?C;yDTF$^Tw8lm1;M?X;q$jarphbEY)ut!nUve=6O%* ztYxNl=lds5^)AnM>(`zcDo0pKXZUVx*zoqIlEdZ~hufDN_+@kX=tZjy z)$FRLCQhpFT`BEyyk<_$j3l=g_ui?Uj+I^TxV3KgCi(Q&(~D;QQQ951saE=Z&#|a; z8OxT*sNZ_Ka9Ynz-^7;Ji6u;?#`Swo{4l&_U?pL^v(0%ov)BEETIW#t`U78|DxE#} zHTQt=oMmV918o(bhzCS2`5E!F^<_B|!)14m`3$D{*?;yN`C!_<-hp*7zfZirLEXvw z!gHQY^{Q72`s}sTUT|gQ(o6F@uIveV>F<(tFVw2#!h@*G4HJ{qpGBK*iF_?pTiDnl zE57%=W?ax&{-CXCqKh~9PF@-`8EvVv`cKOuc(`a$U)w=Nn{DPwo25(RHx}_1GpP!nzVCQ4 z#`qtjSK_xR-@S4Q?_ONCPG((Q_5JYMg&F z)FpBcYjX&F5o!t35 zcB9Q{jakZzm6fi2P+UCy%#QV<_kE@B7k}=3Hoqn1lj;&>Wru$Pm&6TUIJ=$iGC%WU z(k1gZH7kFzYchWOqqEf8Lw`o)w2PY`on3EK_2^mlRr{_N>>oQ8@qDXVWVGbVp~9~j zldY`2MLkF=e*aKg$KV}rle2d5FOQ1l>je$P1JB<)6skV!&f;aFow=)4a_v|T9PQG!A_}V)t0L`Yq!mmpxNczJ!RM=b3&*;7HpIFZU_IZ{$({+Xi?hx$S={e+fw~YUmkQSdl}Jh zFo)&MeUD{nTwLe(6dGSTKI5|6#5vAe7qi|kkl$SrX{)PQX}(`2b*fds);SstHPJd3 zJEXU5T^RpGRQhdS?o+j?qA3EJZ{|<@d@%D|z{-`s&PjgvN{`UeDcNrE;NqF6mEPsQ zVnxMr8QjyjH})mA`dQhD*?o1{zV^ZaiO(Ug#mZ(^ZF?hZ{&>~1Hp9pN;tu|}9WJc7 ztb_mM9RF1N!ke|(4&B>Kbnll%?cVm<;Y`2FvxZ=$GpnZc-*~!OX&dj233I=$;1RdW zFT1>C`o|S-T6mZLDiYDF4VuMx<;@jN?RB0Wt~a+P$}kr!Fw0sfX{E1e_-w)#Z*_qS zQpcAY-Cp!3`PQTp3O;+LSS;+jvB}G1@l~1Hbfcp|rU{L^i>J))ZQ?NBvtp~#OFp-0 zUh3Os?P%Usvsr0LW=h}LbrxpvDK`u})QT-_<7S(_ei=6JsJEVv+$-b1=LNgA7^+sk z-n6gJ-EE!;g?l$&i1k#8_5JfE!)48^f1RSL z$$@WYyz6-3x4&fJw#|&!=k=w1_+h*L?yr2YCC|2NhkJd0pOSFFasF*p+jiIY>d&{= zE`8!zdCOCEu8h~Q>r1w_$z3{h=5|Q+;`yg~G}sTQuszM4SQe}l8Z?pbRmAhXTVjeo z1n&Qvy7bb`{y2V?rnxrSkGKr@7R$xv8HABN9D9rPCAR?J%9Bm z=Sh3>N~W1-FFuj>Ud-E1W?AqKzGHo>Chc)k-0VO5g5I&ks|o^sCM;;2VaAwo)^AaB zz)RKkfb!-4)pT?>1(;Vdd%)C>EAve2HKjA% zpUSZRVb1sN$dtO6yp#Xf`&~9~`4%2gHVRSS-f}N2Dex|HCg-oYwm+M&6g~5a& zp5ay3-Q$-S7#KJUJR*x37`TN%nDNrxx<3pI4D2PIzOL*~nArH#1unZ4G&3+TR(iTP zhGg7(d(+VSu!9Wihv@+x4jU#{v&(8pY9$pE6cq10>$*^OlV!*sreBOLyRIGVTJ0=* zSIa`u#Y?q2cz+{1A6tvX3=vJ2qj4`|Ki`|C`R7OcyL*D!0lE3iFPJov_bD@S>{f_a za%^3!z#ER-#R-ZP_kw@@E2w^woa`#a%N2j>hGp%8mdz2(S=JBDeGCq=2>TQ`b{)@m zZ4gfT_@ISTZAL;2$Dnp{`$?JxHc9d;>dgT3S zhNyu5=OdV29O&8X1dR-YFwSo!v; z<5Qu!=m?S75A*zA-=4JSj%khJp>vm3f4lC?YWZc+0?}DT^79l9-zfZEa&yKq-hDYg z7Oi|O6vio+>!Wz)Wc}_Ec?aL)u@zFnb{`i#xN)T}^4CJHzU!wI8+M7a+DgpXqiTDv zFHdUUikW?9*G|jayM|%Kk-p!FhYCMmuVIq)EAg1R;9VvI0|SGntDnm{r-UW|A)h@K literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.pri b/src/plugins/qmldesigner/components/formeditor/formeditor.pri index f4e345db848..dba3a8a6b74 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditor.pri +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.pri @@ -36,7 +36,8 @@ SOURCES += formeditoritem.cpp \ contentnoteditableindicator.cpp \ backgroundaction.cpp \ formeditortoolbutton.cpp \ - option3daction.cpp + option3daction.cpp \ + formeditorannotationicon.cpp HEADERS += formeditorscene.h \ formeditorwidget.h \ @@ -75,6 +76,7 @@ HEADERS += formeditorscene.h \ contentnoteditableindicator.h \ backgroundaction.h \ formeditortoolbutton.h \ - option3daction.h + option3daction.h \ + formeditorannotationicon.h RESOURCES += formeditor.qrc diff --git a/src/plugins/qmldesigner/components/formeditor/formeditor.qrc b/src/plugins/qmldesigner/components/formeditor/formeditor.qrc index 3da1cfc3ce0..45170bde01d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditor.qrc +++ b/src/plugins/qmldesigner/components/formeditor/formeditor.qrc @@ -6,5 +6,7 @@ no_snapping@2x.png snapping_and_anchoring.png snapping_and_anchoring@2x.png + annotationsIcon.png + annotationsIconActive.png diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp new file mode 100644 index 00000000000..367635d59f0 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** 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 "formeditorannotationicon.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace QmlDesigner { + +FormEditorAnnotationIcon::FormEditorAnnotationIcon(const ModelNode &modelNode, QGraphicsItem *parent) + : QGraphicsObject(parent) + , m_modelNode(modelNode) + , m_readerIsActive(false) + , m_customId(modelNode.customId()) + , m_annotation(modelNode.annotation()) + , m_annotationEditor(nullptr) + , m_normalIconStr(":icon/layout/annotationsIcon.png") + , m_activeIconStr(":icon/layout/annotationsIconActive.png") + , m_iconW(40) + , m_iconH(32) +{ + setAcceptHoverEvents(true); + + bool hasAuxData = modelNode.hasAnnotation() || modelNode.hasCustomId(); + + setEnabled(hasAuxData); + setVisible(hasAuxData); + + FormEditorScene *scene = qobject_cast(parentItem()->scene()); + if (scene) { + m_readerIsActive = scene->annotationVisibility(); + if (m_readerIsActive) { + drawReader(); + } + } + + setToolTip(tr("Annotation")); + setCursor(Qt::ArrowCursor); +} + +FormEditorAnnotationIcon::~FormEditorAnnotationIcon() +{ + if (m_annotationEditor) { + m_annotationEditor->deleteLater(); + } +} + +void FormEditorAnnotationIcon::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + painter->setPen(Qt::NoPen); + + if (!isEnabled()) + setOpacity(0.5); + + bool hasAuxData = m_modelNode.hasAnnotation() || m_modelNode.hasCustomId(); + + if (hasAuxData) { + FormEditorScene *scene = qobject_cast(parentItem()->scene()); + if (scene) + m_readerIsActive = scene->annotationVisibility(); + + QPixmap icon( (m_readerIsActive ? m_activeIconStr : m_normalIconStr) ); + + painter->drawPixmap(0, 0, + static_cast(m_iconW), static_cast(m_iconH), + icon); + + m_customId = m_modelNode.customId(); + m_annotation = m_modelNode.annotation(); + + if (m_readerIsActive) + resetReader(); + } + else { + hideReader(); + } + + setEnabled(hasAuxData); + setVisible(hasAuxData); + + painter->restore(); +} + +QRectF FormEditorAnnotationIcon::boundingRect() const +{ + return QRectF(0, 0, m_iconW, m_iconH); +} + +qreal FormEditorAnnotationIcon::iconWidth() +{ + return m_iconW; +} + +qreal FormEditorAnnotationIcon::iconHeight() +{ + return m_iconH; +} + +bool FormEditorAnnotationIcon::isReaderActive() +{ + return m_readerIsActive; +} + +void FormEditorAnnotationIcon::setActive(bool readerStatus) +{ + m_readerIsActive = readerStatus; + + if (m_readerIsActive) + resetReader(); + else + hideReader(); + + update(); +} + +void FormEditorAnnotationIcon::hoverEnterEvent(QGraphicsSceneHoverEvent * event) +{ + QGraphicsItem::hoverEnterEvent(event); + event->accept(); + update(); +} + +void FormEditorAnnotationIcon::hoverLeaveEvent(QGraphicsSceneHoverEvent * event) +{ + QGraphicsItem::hoverLeaveEvent(event); + event->accept(); + update(); +} + +void FormEditorAnnotationIcon::hoverMoveEvent(QGraphicsSceneHoverEvent * event) +{ + QGraphicsItem::hoverMoveEvent(event); +} + +void FormEditorAnnotationIcon::mousePressEvent(QGraphicsSceneMouseEvent * event) +{ + event->accept(); + Qt::MouseButton button = event->button(); + + if (button == Qt::LeftButton) { + if (m_readerIsActive) { + hideReader(); + m_readerIsActive = false; + } else { + drawReader(); + m_readerIsActive = true; + } + } + + FormEditorScene *scene = qobject_cast(parentItem()->scene()); + if (scene) + scene->setAnnotationVisibility(m_readerIsActive); + + update(); +} + +void FormEditorAnnotationIcon::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) +{ + event->accept(); +} + +void FormEditorAnnotationIcon::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + QMenu menu; + menu.addAction(tr("Edit Annotation"), [this]() { + createAnnotationEditor(); + }); + + menu.addAction(tr("Remove Annotation"), [this]() { + removeAnnotationDialog(); + }); + + menu.exec(event->screenPos()); + event->accept(); +} + +void FormEditorAnnotationIcon::resetReader() +{ + hideReader(); + drawReader(); +} + +void FormEditorAnnotationIcon::drawReader() +{ + const qreal width = 290; + const qreal height = 200; + const qreal offset = 5; + + const QRectF titleRect(0, 0, width, 30); + const QPointF cornerPosition(m_iconW + offset, 0); + + QGraphicsItem *titleBubble = createTitleBubble(titleRect, m_customId, this); + titleBubble->setPos(cornerPosition); + + if (m_annotation.hasComments()) { + QList comments; + + QPointF commentPosition(cornerPosition.x(), 40); + QRectF commentRect(0, 0, width, height); + + for (const Comment &comment : m_annotation.comments()) { + QGraphicsItem *commentBubble = createCommentBubble(commentRect, comment.title(), + comment.author(), comment.text(), + comment.timestampStr(), this); + commentBubble->setPos(commentPosition); + + commentPosition += QPointF(width + offset, 0); + comments.push_back(commentBubble); + } + + + int currentColumn = 0; + qreal columnHeight = 0; + const qreal maxHeight = 650; + const QPointF commentsStartPosition(cornerPosition.x(), cornerPosition.y() + titleRect.height() + (offset*2)); + QPointF newPos(commentsStartPosition); + + for (QGraphicsItem *comment : comments) { + qreal itemHeight = comment->boundingRect().height(); + + if ((columnHeight + offset + itemHeight) > maxHeight) { + // have no extra space + columnHeight = 0; + ++currentColumn; + + newPos = commentsStartPosition + QPointF(currentColumn * (offset + width), 0); + + } else { + //few normal comments, lets stack them + } + + columnHeight += itemHeight + offset; + + comment->setPos(newPos); + + newPos += QPointF(0, itemHeight + offset); + } + } +} + +void FormEditorAnnotationIcon::hideReader() +{ + if (!childItems().isEmpty()) { + for (QGraphicsItem *item : childItems()) { + delete item; + } + } +} + +QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(const QRectF &rect, const QString &title, + const QString &author, const QString &text, + const QString &date, QGraphicsItem *parent) +{ + static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor); + static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker); + static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor); + QFont font; + font.setBold(true); + + QGraphicsRectItem *frameItem = new QGraphicsRectItem(rect, parent); + + QGraphicsTextItem *titleItem = new QGraphicsTextItem(frameItem); + titleItem->setPlainText(title); + titleItem->setFont(font); + titleItem->setDefaultTextColor(textColor); + titleItem->setTextWidth(rect.width()); + titleItem->update(); + + QGraphicsTextItem *authorItem = new QGraphicsTextItem(frameItem); + authorItem->setPlainText(tr("By: ") + author); + authorItem->setDefaultTextColor(textColor); + authorItem->setTextWidth(rect.width()); + authorItem->setPos(titleItem->x(), titleItem->boundingRect().height() + titleItem->y()); + authorItem->update(); + + QGraphicsTextItem *textItem = new QGraphicsTextItem(frameItem); + textItem->setPlainText(text); + textItem->setDefaultTextColor(textColor); + textItem->setTextWidth(rect.width()); + textItem->setPos(authorItem->x(), authorItem->boundingRect().height() + authorItem->y() + 5); + textItem->update(); + + qreal contentRect = titleItem->boundingRect().height() + + authorItem->boundingRect().height() + + textItem->boundingRect().height(); + + if ((contentRect + 60) > rect.height()) { + frameItem->setRect(rect.x(), rect.y(), rect.width(), contentRect+60); + } + + QGraphicsTextItem *dateItem = new QGraphicsTextItem(frameItem); + dateItem->setPlainText(tr("Edited: ") + date); + dateItem->setDefaultTextColor(textColor); + dateItem->setTextWidth(rect.width()); + dateItem->setPos(frameItem->boundingRect().bottomLeft() + QPointF(0, -30)); + dateItem->update(); + + QPen pen; + pen.setCosmetic(true); + pen.setWidth(2); + pen.setCapStyle(Qt::RoundCap); + pen.setJoinStyle(Qt::BevelJoin); + pen.setColor(frameColor); + + frameItem->setPen(pen); //outline + frameItem->setBrush(backgroundColor); //back + frameItem->update(); + + return frameItem; +} + +QGraphicsItem *FormEditorAnnotationIcon::createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent) +{ + static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor); + static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker); + static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor); + QFont font; + font.setBold(true); + + QGraphicsRectItem *frameItem = new QGraphicsRectItem(rect, parent); + QGraphicsTextItem *titleItem = new QGraphicsTextItem(text, frameItem); + + titleItem->setDefaultTextColor(textColor); + titleItem->setFont(font); + titleItem->update(); + + if (titleItem->boundingRect().width() > rect.width()) { + frameItem->setRect(QRectF(rect.x(), rect.y(), + titleItem->boundingRect().width(), rect.height())); + } + + QPen pen; + pen.setCosmetic(true); + pen.setWidth(2); + pen.setCapStyle(Qt::RoundCap); + pen.setJoinStyle(Qt::BevelJoin); + pen.setColor(frameColor); + + frameItem->setPen(pen); //outline + frameItem->setBrush(backgroundColor); //back + frameItem->update(); + + return frameItem; +} + +void FormEditorAnnotationIcon::createAnnotationEditor() +{ + if (m_annotationEditor) { + m_annotationEditor->close(); + m_annotationEditor->deleteLater(); + m_annotationEditor = nullptr; + } + + m_annotationEditor = new AnnotationEditorDialog(Core::ICore::dialogParent(), + m_modelNode.displayName(), + m_modelNode.customId(), + m_modelNode.annotation()); + + connect(m_annotationEditor, &AnnotationEditorDialog::accepted, + this, &FormEditorAnnotationIcon::annotationDialogAccepted); + connect(m_annotationEditor, &QDialog::rejected, + this, &FormEditorAnnotationIcon::annotationDialogRejected); + + m_annotationEditor->open(); +} + +void FormEditorAnnotationIcon::removeAnnotationDialog() +{ + QString dialogTitle = tr("Annotation"); + if (!m_customId.isNull()) { + dialogTitle = m_customId; + } + QMessageBox *deleteDialog = new QMessageBox(Core::ICore::dialogParent()); + deleteDialog->setWindowTitle(dialogTitle); + deleteDialog->setText(tr("Delete this annotation?")); + deleteDialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + deleteDialog->setDefaultButton(QMessageBox::Yes); + + int result = deleteDialog->exec(); + if (deleteDialog) deleteDialog->deleteLater(); + + if (result == QMessageBox::Yes) { + m_modelNode.removeCustomId(); + m_modelNode.removeAnnotation(); + update(); + } +} + +void FormEditorAnnotationIcon::annotationDialogAccepted() +{ + if (m_annotationEditor) { + QString customId = m_annotationEditor->customId(); + m_customId = customId; + m_modelNode.setCustomId(customId); + + Annotation annotation = m_annotationEditor->annotation(); + + if (annotation.comments().isEmpty()) + m_modelNode.removeAnnotation(); + else + m_modelNode.setAnnotation(annotation); + + m_annotation = annotation; + + m_annotationEditor->close(); + m_annotationEditor->deleteLater(); + } + + m_annotationEditor = nullptr; + + if (m_readerIsActive) + resetReader(); +} + +void FormEditorAnnotationIcon::annotationDialogRejected() +{ + if (m_annotationEditor) { + m_annotationEditor->close(); + m_annotationEditor->deleteLater(); + } + + m_annotationEditor = nullptr; +} + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.h b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.h new file mode 100644 index 00000000000..c11ea5a3b77 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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 + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + + +namespace QmlDesigner { + +class AnnotationEditorDialog; + +class FormEditorAnnotationIcon : public QGraphicsObject +{ + Q_OBJECT + +public: + explicit FormEditorAnnotationIcon(const ModelNode &modelNode, QGraphicsItem *parent = nullptr); + ~FormEditorAnnotationIcon() override; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + QRectF boundingRect() const override; + + qreal iconWidth(); + qreal iconHeight(); + + bool isReaderActive(); + void setActive(bool readerStatus); + + void resetReader(); + +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + +private: + void drawReader(); + void hideReader(); + QGraphicsItem *createCommentBubble(const QRectF &rect, const QString &title, + const QString &author, const QString &text, + const QString &date, QGraphicsItem *parent); + QGraphicsItem *createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent); + + void createAnnotationEditor(); + void removeAnnotationDialog(); + + void annotationDialogAccepted(); + void annotationDialogRejected(); + +private: + ModelNode m_modelNode; + bool m_readerIsActive; + QString m_customId; + Annotation m_annotation; + AnnotationEditorDialog *m_annotationEditor; + + QString m_normalIconStr; + QString m_activeIconStr; + + qreal m_iconW; + qreal m_iconH; +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp index ea38ad13682..3f2e83bf1ac 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp @@ -49,9 +49,10 @@ namespace QmlDesigner { FormEditorScene::FormEditorScene(FormEditorWidget *view, FormEditorView *editorView) - : QGraphicsScene(), - m_editorView(editorView), - m_showBoundingRects(false) + : QGraphicsScene() + , m_editorView(editorView) + , m_showBoundingRects(false) + , m_annotationVisibility(false) { setupScene(); view->setScene(this); @@ -431,5 +432,15 @@ bool FormEditorScene::showBoundingRects() const return m_showBoundingRects; } +bool FormEditorScene::annotationVisibility() const +{ + return m_annotationVisibility; +} + +void FormEditorScene::setAnnotationVisibility(bool status) +{ + m_annotationVisibility = status; +} + } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h index 9dba7d23cb3..5c4f0875755 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h @@ -95,6 +95,9 @@ public: void setShowBoundingRects(bool show); bool showBoundingRects() const; + bool annotationVisibility() const; + void setAnnotationVisibility(bool status); + protected: bool event(QEvent *event) override; void dropEvent(QGraphicsSceneDragDropEvent * event) override; @@ -129,6 +132,7 @@ private: QPointer m_manipulatorLayerItem; ModelNode m_dragNode; bool m_showBoundingRects; + bool m_annotationVisibility; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index c6e0516e139..44e514641a5 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -501,6 +501,7 @@ void FormEditorView::changeCurrentToolTo(AbstractFormEditorTool *newTool) m_currentTool->clear(); m_currentTool->setItems(scene()->itemsForQmlItemNodes(toQmlItemNodeList( selectedModelNodes()))); + m_currentTool->start(); } @@ -530,6 +531,10 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, const PropertyN FormEditorItem *editorItem = m_scene->itemForQmlItemNode(item); if (editorItem) editorItem->update(); + } else if (name == "annotation" || name == "customId") { + if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item)) { + editorItem->update(); + } } } diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp index 61dbe75ea32..f4af3f481f7 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp @@ -26,6 +26,8 @@ #include "selectionindicator.h" #include +#include "annotationeditor/annotation.h" +#include #include #include @@ -42,6 +44,7 @@ namespace QmlDesigner { SelectionIndicator::SelectionIndicator(LayerItem *layerItem) : m_layerItem(layerItem) + , m_annotationItem(nullptr) { } @@ -75,6 +78,7 @@ void SelectionIndicator::clear() } } m_labelItem.reset(nullptr); + m_annotationItem = nullptr; m_indicatorShapeHash.clear(); } @@ -119,6 +123,7 @@ void SelectionIndicator::setItems(const QList &itemList) if (checkSingleSelection(itemList)) { FormEditorItem *selectedItem = itemList.constFirst(); m_labelItem = std::make_unique(m_layerItem.data()); + const qreal scaleFactor = m_layerItem->viewportTransform().m11(); QGraphicsWidget *toolbar = DesignerActionManager::instance().createFormEditorToolBar(m_labelItem.get()); toolbar->setPos(1, -1); @@ -129,6 +134,14 @@ void SelectionIndicator::setItems(const QList &itemList) if (modelNode.hasId()) textItem->setPlainText(modelNode.id()); + if (modelNode.hasAnnotation() || modelNode.hasCustomId()) { + m_annotationItem = new FormEditorAnnotationIcon(modelNode, m_labelItem.get()); + m_annotationItem->update(); + } + else { + m_annotationItem = nullptr; + } + static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor); textItem->setDefaultTextColor(textColor); @@ -139,18 +152,25 @@ void SelectionIndicator::setItems(const QList &itemList) QPointF pos = labelRect.topLeft(); labelRect.moveTo(0, 0); m_labelItem->setPolygon(labelRect); - const int scaledHeight = labelHeight / m_layerItem->viewportTransform().m11(); + const int scaledHeight = labelHeight / scaleFactor; m_labelItem->setPos(pos + QPointF(0, -scaledHeight)); const int offset = (labelHeight - textItem->boundingRect().height()) / 2; textItem->setPos(QPointF(toolbar->size().width(), offset)); m_labelItem->setFlag(QGraphicsItem::ItemIsSelectable, false); m_labelItem->setFlag(QGraphicsItem::ItemIgnoresTransformations, true); + QPen pen; pen.setCosmetic(true); pen.setWidth(2); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::BevelJoin); pen.setColor(selectionColor); + + if (m_annotationItem) { + m_annotationItem->setFlags(QGraphicsItem::ItemIgnoresTransformations); + adjustAnnotationPosition(labelPolygon.boundingRect(), m_labelItem->boundingRect(), scaleFactor); + } + m_labelItem->setPen(pen); m_labelItem->setBrush(selectionColor); m_labelItem->update(); @@ -172,8 +192,14 @@ void SelectionIndicator::updateItems(const QList &itemList) QPolygonF labelPolygon = boundingRectInLayerItemSpaceForItem(selectedItem, m_layerItem.data()); QRectF labelRect = labelPolygon.boundingRect(); QPointF pos = labelRect.topLeft(); - const int scaledHeight = labelHeight / m_layerItem->viewportTransform().m11(); + const qreal scaleFactor = m_layerItem->viewportTransform().m11(); + const int scaledHeight = labelHeight / scaleFactor; m_labelItem->setPos(pos + QPointF(0, -scaledHeight)); + + if (m_annotationItem) { + adjustAnnotationPosition(labelPolygon.boundingRect(), m_labelItem->boundingRect(), scaleFactor); + } + m_layerItem->update(); } } @@ -186,5 +212,24 @@ void SelectionIndicator::setCursor(const QCursor &cursor) item->setCursor(cursor); } +void SelectionIndicator::adjustAnnotationPosition(const QRectF &itemRect, const QRectF &labelRect, qreal scaleFactor) +{ + if (!m_annotationItem) return; + + const qreal iconW = 40 * 0.5; //*0.5 for a shift of an icon outide the item + qreal iconX = 0.0; + qreal iconY = -15.0/scaleFactor; //small offset + + if (((labelRect.width() + iconW)/scaleFactor) > itemRect.width()) + iconY -= labelRect.height()/scaleFactor; + + if ((iconW/scaleFactor) > itemRect.width()) + iconX = 0.0; + else + iconX = (itemRect.width()) - (iconW/scaleFactor); + + m_annotationItem->setPos(iconX*scaleFactor, iconY*scaleFactor); +} + } diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.h b/src/plugins/qmldesigner/components/formeditor/selectionindicator.h index 7da3737a0f3..252020e9904 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.h +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.h @@ -35,6 +35,8 @@ namespace QmlDesigner { +class FormEditorAnnotationIcon; + class SelectionIndicator { public: @@ -50,12 +52,16 @@ public: void updateItems(const QList &itemList); void setCursor(const QCursor &cursor); +private: + void adjustAnnotationPosition(const QRectF &itemRect, const QRectF &labelRect, qreal scaleFactor); private: QHash m_indicatorShapeHash; + FormEditorItem *m_selectedItem; QPointer m_layerItem; QCursor m_cursor; std::unique_ptr m_labelItem; + FormEditorAnnotationIcon *m_annotationItem; //handled by m_labelItem }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 5cb3766dadc..7497879ecb5 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -159,6 +159,8 @@ QVariant properDefaultAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, return 0; else if (propertyName == "break") return 50; + else if (propertyName == "customId") + return QString(); return {}; } @@ -221,6 +223,8 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml PropertyNameList propertyNames; + propertyNames.append("customId"); + if (itemNode.isFlowTransition()) { propertyNames.append({"color", "width", "inOffset", "outOffset", "dash", "break"}); } else if (itemNode.isFlowItem()) { diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 131ef0fe8a2..7f705e43c6a 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -34,6 +34,7 @@ #include "simplecolorpalettemodel.h" #include "bindingeditor/bindingeditor.h" #include "bindingeditor/actioneditor.h" +#include "annotationeditor/annotationeditor.h" #include "qmlanchorbindingproxy.h" #include "theme.h" #include "aligndistribute.h" @@ -63,6 +64,7 @@ void Quick2PropertyEditorView::registerQmlTypes() Internal::QmlAnchorBindingProxy::registerDeclarativeType(); BindingEditor::registerDeclarativeType(); ActionEditor::registerDeclarativeType(); + AnnotationEditor::registerDeclarativeType(); AlignDistribute::registerDeclarativeType(); Tooltip::registerDeclarativeType(); } diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 253250af241..ac65de0240f 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -28,6 +28,7 @@ #include "qmldesignercorelib_global.h" #include #include +#include #include QT_BEGIN_NAMESPACE @@ -56,6 +57,8 @@ class NodeListProperty; class NodeProperty; class NodeAbstractProperty; class ModelNode; +class Comment; +class Annotation; QMLDESIGNERCORE_EXPORT QList toInternalNodeList(const QList &nodeList); @@ -183,6 +186,22 @@ public: bool hasAuxiliaryData(const PropertyName &name) const; QHash auxiliaryData() const; + QString customId() const; + bool hasCustomId() const; + void setCustomId(const QString &str); + void removeCustomId(); + + QVector comments() const; + bool hasComments() const; + void setComments(const QVector &coms); + void addComment(const Comment &com); + bool updateComment(const Comment &com, int position); + + Annotation annotation() const; + bool hasAnnotation() const; + void setAnnotation(const Annotation &annotation); + void removeAnnotation(); + qint32 internalId() const; void setNodeSource(const QString&); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 17c5a608104..7a77044e13c 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -41,6 +41,7 @@ #include "nodelistproperty.h" #include "nodeproperty.h" #include +#include "annotationeditor/annotation.h" #include @@ -1052,6 +1053,100 @@ QHash ModelNode::auxiliaryData() const return internalNode()->auxiliaryData(); } +QString ModelNode::customId() const +{ + QString result; + if (hasCustomId()) + result = auxiliaryData(customIdProperty).value(); + + return result; +} + +bool ModelNode::hasCustomId() const +{ + return hasAuxiliaryData(customIdProperty); +} + +void ModelNode::setCustomId(const QString &str) +{ + setAuxiliaryData(customIdProperty, QVariant::fromValue(str)); +} + +void ModelNode::removeCustomId() +{ + if (hasCustomId()) { + removeAuxiliaryData(customIdProperty); + } +} + +QVector ModelNode::comments() const +{ + return annotation().comments(); +} + +bool ModelNode::hasComments() const +{ + return annotation().hasComments(); +} + +void ModelNode::setComments(const QVector &coms) +{ + Annotation anno = annotation(); + anno.setComments(coms); + + setAnnotation(anno); +} + +void ModelNode::addComment(const Comment &com) +{ + Annotation anno = annotation(); + anno.addComment(com); + + setAnnotation(anno); +} + +bool ModelNode::updateComment(const Comment &com, int position) +{ + bool result = false; + if (hasAnnotation()) { + Annotation anno = annotation(); + + if (anno.updateComment(com, position)) { + setAnnotation(anno); + result = true; + } + } + + return result; +} + +Annotation ModelNode::annotation() const +{ + Annotation result; + + if (hasAnnotation()) + result.fromQString(auxiliaryData(annotationProperty).value()); + + return result; +} + +bool ModelNode::hasAnnotation() const +{ + return hasAuxiliaryData(annotationProperty); +} + +void ModelNode::setAnnotation(const Annotation &annotation) +{ + setAuxiliaryData(annotationProperty, QVariant::fromValue(annotation.toQString())); +} + +void ModelNode::removeAnnotation() +{ + if (hasAnnotation()) { + removeAuxiliaryData(annotationProperty); + } +} + void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { model()->d->setScriptFunctions(internalNode(), scriptFunctionList); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 8f8a3946023..8dad77ad44c 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -530,6 +530,14 @@ QString RewriterView::auxiliaryDataAsQML() const if (metaType == QMetaType::QString || metaType == QMetaType::QColor) { + + strValue.replace(QStringLiteral("\\"), QStringLiteral("\\\\")); + strValue.replace(QStringLiteral("\""), QStringLiteral("\\\"")); + strValue.replace(QStringLiteral("\t"), QStringLiteral("\\t")); + strValue.replace(QStringLiteral("\r"), QStringLiteral("\\r")); + strValue.replace(QStringLiteral("\n"), QStringLiteral("\\n")); + strValue.replace(QStringLiteral("*/"), QStringLiteral("*\\/")); + strValue = "\"" + strValue + "\""; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 715e64a3e91..c1249f3dbdc 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -238,6 +239,7 @@ bool QmlDesignerPlugin::delayedInitialize() d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::SourceTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::ColorTool); + d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::TextTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::PathTool); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index 625770cb723..30c34209377 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -29,6 +29,7 @@ include(components/timelineeditor/timelineeditor.pri) include(components/connectioneditor/connectioneditor.pri) include(components/curveeditor/curveeditor.pri) include(components/bindingeditor/bindingeditor.pri) +include(components/annotationeditor/annotationeditor.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 d997b63425f..b6939808f2a 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -461,6 +461,8 @@ Project { "formeditor/dragtool.cpp", "formeditor/dragtool.h", "formeditor/formeditor.qrc", + "formeditor/formeditorannotationicon.cpp", + "formeditor/formeditorannotationicon.h", "formeditor/formeditorgraphicsview.cpp", "formeditor/formeditorgraphicsview.h", "formeditor/formeditoritem.cpp", @@ -636,6 +638,18 @@ Project { name: "extension" prefix: "components/" files: [ + "annotationeditor/annotation.cpp", + "annotationeditor/annotation.h", + "annotationeditor/annotationcommenttab.cpp", + "annotationeditor/annotationcommenttab.h", + "annotationeditor/annotationcommenttab.ui", + "annotationeditor/annotationeditor.cpp", + "annotationeditor/annotationeditor.h", + "annotationeditor/annotationeditordialog.cpp", + "annotationeditor/annotationeditordialog.h", + "annotationeditor/annotationeditordialog.ui + "annotationeditor/annotationtool.cpp", + "annotationeditor/annotationtool.h", "bindingeditor/bindingeditor.cpp", "bindingeditor/bindingeditor.h", "bindingeditor/actioneditor.cpp", From a467e34e58807fe9f63e49e01fb6ee92fd48e3c1 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 20 May 2019 15:07:29 +0200 Subject: [PATCH 4/4] Fix qdoc call after it moved to LLVM/Clang For some unknown reason qdoc does not add the include paths for Qt modules, so we need to do that manually. Also, if we do that, we need to pass it the default include paths, so standard C++ headers are found. Qt includes and standard headers are found now, but Framework style includes on macOS still fail ("QtCore/QString"), which are used in Qt headers as well. Looks like the framework path that is passed with -F to qdoc is not passed on correctly. Task-number: QTCREATORBUG-22451 Change-Id: I04b2c75ecdeb1f0e70ba9adfea039f0ff16ec96b Reviewed-by: Leena Miettinen --- doc/config/qt-defines.qdocconf | 3 +-- doc/doc_targets.pri | 2 +- docs.pri | 13 +++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/doc/config/qt-defines.qdocconf b/doc/config/qt-defines.qdocconf index b8daa054972..faef889fec8 100644 --- a/doc/config/qt-defines.qdocconf +++ b/doc/config/qt-defines.qdocconf @@ -9,7 +9,6 @@ defines = Q_QDOC \ Q_OS_.* \ Q_BYTE_ORDER \ QT_DEPRECATED \ - Q_NO_USING_KEYWORD \ - __cplusplus + Q_NO_USING_KEYWORD versionsym = QT_VERSION_STR diff --git a/doc/doc_targets.pri b/doc/doc_targets.pri index 808d5e9fcfd..23fd0fee717 100644 --- a/doc/doc_targets.pri +++ b/doc/doc_targets.pri @@ -41,7 +41,7 @@ for (doc_file, DOC_FILES) { DOC_TARGETDIR = $$DOC_TARGET DOC_OUTPUTDIR = $${DOCS_BASE_OUTDIR}/$${DOC_TARGETDIR}$${DOC_OUTDIR_POSTFIX} - html_docs_$${DOC_TARGET}.commands = $$QDOC -outputdir $$shell_quote($$DOC_OUTPUTDIR) $$doc_file $$DOC_INDEXES + html_docs_$${DOC_TARGET}.commands = $$QDOC -outputdir $$shell_quote($$DOC_OUTPUTDIR) $$doc_file $$DOC_INDEXES $$DOC_INCLUDES QMAKE_EXTRA_TARGETS += html_docs_$${DOC_TARGET} !isEmpty(html_docs.commands): html_docs.commands += && diff --git a/docs.pri b/docs.pri index 3f37d7f4e7d..48d22a458b5 100644 --- a/docs.pri +++ b/docs.pri @@ -41,4 +41,17 @@ DOC_HTML_INSTALLDIR = $$INSTALL_DOC_PATH DOC_QCH_OUTDIR = $$IDE_DOC_PATH DOC_QCH_INSTALLDIR = $$INSTALL_DOC_PATH +minQtVersion(5, 11, 0) { + for (include_path, INCLUDEPATH): \ + DOC_INCLUDES += -I $$shell_quote($$include_path) + for (module, QT) { + MOD_INCLUDES = $$eval(QT.$${module}.includes) + for (include_path, MOD_INCLUDES): \ + DOC_INCLUDES += -I $$shell_quote($$include_path) + } + for (include_path, QMAKE_DEFAULT_INCDIRS): \ + DOC_INCLUDES += -I $$shell_quote($$include_path) + macos: DOC_INCLUDES += -F $$shell_quote($$[QT_INSTALL_LIBS]) +} + include(doc/doc_targets.pri)