Merge remote-tracking branch 'origin/4.13'

Change-Id: Ie67085fcbb4150c60f0253312e19a82b95b4a898
This commit is contained in:
Eike Ziller
2020-06-29 09:01:31 +02:00
130 changed files with 7698 additions and 321 deletions

View File

@@ -57,11 +57,11 @@
To use \QC to develop Qt applications for Android, you need the following: To use \QC to develop Qt applications for Android, you need the following:
\list \list
\li \l{https://www.oracle.com/java/technologies/javase-jdk8-downloads.html} \li \l{AdoptOpenJDK} for all platforms. You can also use \l{OpenJDK}
{Java SE Development Kit (JDK)} version 6 up to 8. on Linux.
You can also use \l{http://openjdk.java.net/}{OpenJDK} on Linux.
\note Android SDK Tools have issues with JDK versions later than 8. \note Android SDK Tools versions <= 26.x have issues with JDK versions
later than 8. It is recommended to use the latest Command-line SDK Tools.
\li \l{http://www.gradle.org}{Gradle} for building application packages \li \l{http://www.gradle.org}{Gradle} for building application packages
(APK) and app bundles (AAB) for Android devices. Gradle is delivered (APK) and app bundles (AAB) for Android devices. Gradle is delivered

View File

@@ -59,7 +59,7 @@
\section1 Adding CMake Tools \section1 Adding CMake Tools
\QC requires CMake's {https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} \QC requires CMake's \l{https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html}
{file-based API}. Please make sure to use CMake version 3.14, or later. {file-based API}. Please make sure to use CMake version 3.14, or later.
To specify paths to CMake executables: To specify paths to CMake executables:

View File

@@ -45,3 +45,11 @@
\externalpage https://doc.qt.io/QtForMCUs/qtul-getting-started-windows.html \externalpage https://doc.qt.io/QtForMCUs/qtul-getting-started-windows.html
\title Getting Started on Windows \title Getting Started on Windows
*/ */
/*!
\externalpage https://adoptopenjdk.net/
\title AdoptOpenJDK
*/
/*!
\externalpage http://openjdk.java.net
\title OpenJDK
*/

View File

@@ -40,7 +40,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector<InstanceContainer> &instanc
const QVector<AddImportContainer> &importVector, const QVector<AddImportContainer> &importVector,
const QVector<MockupTypeContainer> &mockupTypeVector, const QVector<MockupTypeContainer> &mockupTypeVector,
const QUrl &fileUrl, const QUrl &fileUrl,
const QHash<QString, QVariantMap> &edit3dToolStates) const QHash<QString, QVariantMap> &edit3dToolStates,
const QString &language)
: m_instanceVector(instanceContainer), : m_instanceVector(instanceContainer),
m_reparentInstanceVector(reparentContainer), m_reparentInstanceVector(reparentContainer),
m_idVector(idVector), m_idVector(idVector),
@@ -50,7 +51,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector<InstanceContainer> &instanc
m_importVector(importVector), m_importVector(importVector),
m_mockupTypeVector(mockupTypeVector), m_mockupTypeVector(mockupTypeVector),
m_fileUrl(fileUrl), m_fileUrl(fileUrl),
m_edit3dToolStates(edit3dToolStates) m_edit3dToolStates(edit3dToolStates),
m_language(language)
{ {
} }
@@ -104,6 +106,11 @@ QHash<QString, QVariantMap> CreateSceneCommand::edit3dToolStates() const
return m_edit3dToolStates; return m_edit3dToolStates;
} }
QString CreateSceneCommand::language() const
{
return m_language;
}
QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command) QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command)
{ {
out << command.instances(); out << command.instances();
@@ -116,6 +123,7 @@ QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command)
out << command.mockupTypes(); out << command.mockupTypes();
out << command.fileUrl(); out << command.fileUrl();
out << command.edit3dToolStates(); out << command.edit3dToolStates();
out << command.language();
return out; return out;
} }
@@ -132,6 +140,7 @@ QDataStream &operator>>(QDataStream &in, CreateSceneCommand &command)
in >> command.m_mockupTypeVector; in >> command.m_mockupTypeVector;
in >> command.m_fileUrl; in >> command.m_fileUrl;
in >> command.m_edit3dToolStates; in >> command.m_edit3dToolStates;
in >> command.m_language;
return in; return in;
} }
@@ -148,7 +157,8 @@ QDebug operator <<(QDebug debug, const CreateSceneCommand &command)
<< "imports: " << command.imports() << ", " << "imports: " << command.imports() << ", "
<< "mockupTypes: " << command.mockupTypes() << ", " << "mockupTypes: " << command.mockupTypes() << ", "
<< "fileUrl: " << command.fileUrl() << ", " << "fileUrl: " << command.fileUrl() << ", "
<< "edit3dToolStates: " << command.edit3dToolStates() << ")"; << "edit3dToolStates: " << command.edit3dToolStates() << ", "
<< "language: " << command.language() << ")";
} }
} }

View File

@@ -45,16 +45,18 @@ class CreateSceneCommand
public: public:
CreateSceneCommand(); CreateSceneCommand();
explicit CreateSceneCommand(const QVector<InstanceContainer> &instanceContainer, explicit CreateSceneCommand(
const QVector<ReparentContainer> &reparentContainer, const QVector<InstanceContainer> &instanceContainer,
const QVector<IdContainer> &idVector, const QVector<ReparentContainer> &reparentContainer,
const QVector<PropertyValueContainer> &valueChangeVector, const QVector<IdContainer> &idVector,
const QVector<PropertyBindingContainer> &bindingChangeVector, const QVector<PropertyValueContainer> &valueChangeVector,
const QVector<PropertyValueContainer> &auxiliaryChangeVector, const QVector<PropertyBindingContainer> &bindingChangeVector,
const QVector<AddImportContainer> &importVector, const QVector<PropertyValueContainer> &auxiliaryChangeVector,
const QVector<MockupTypeContainer> &mockupTypeVector, const QVector<AddImportContainer> &importVector,
const QUrl &fileUrl, const QVector<MockupTypeContainer> &mockupTypeVector,
const QHash<QString, QVariantMap> &edit3dToolStates); const QUrl &fileUrl,
const QHash<QString, QVariantMap> &edit3dToolStates,
const QString &language);
QVector<InstanceContainer> instances() const; QVector<InstanceContainer> instances() const;
QVector<ReparentContainer> reparentInstances() const; QVector<ReparentContainer> reparentInstances() const;
@@ -66,6 +68,7 @@ public:
QVector<MockupTypeContainer> mockupTypes() const; QVector<MockupTypeContainer> mockupTypes() const;
QUrl fileUrl() const; QUrl fileUrl() const;
QHash<QString, QVariantMap> edit3dToolStates() const; QHash<QString, QVariantMap> edit3dToolStates() const;
QString language() const;
private: private:
QVector<InstanceContainer> m_instanceVector; QVector<InstanceContainer> m_instanceVector;
@@ -78,6 +81,7 @@ private:
QVector<MockupTypeContainer> m_mockupTypeVector; QVector<MockupTypeContainer> m_mockupTypeVector;
QUrl m_fileUrl; QUrl m_fileUrl;
QHash<QString, QVariantMap> m_edit3dToolStates; QHash<QString, QVariantMap> m_edit3dToolStates;
QString m_language;
}; };
QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command); QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command);

View File

@@ -104,6 +104,7 @@ Model {
grabsMouse: targetNode grabsMouse: targetNode
active: rootModel.active active: rootModel.active
dragHelper: rootModel.dragHelper dragHelper: rootModel.dragHelper
priority: 5
onPressed: rootModel.handlePressed(mouseAreaYZ, planePos, screenPos) onPressed: rootModel.handlePressed(mouseAreaYZ, planePos, screenPos)
onDragged: rootModel.handleDragged(mouseAreaYZ, planePos, screenPos) onDragged: rootModel.handleDragged(mouseAreaYZ, planePos, screenPos)
@@ -121,6 +122,7 @@ Model {
grabsMouse: targetNode grabsMouse: targetNode
active: rootModel.active active: rootModel.active
dragHelper: rootModel.dragHelper dragHelper: rootModel.dragHelper
priority: 5
onPressed: rootModel.handlePressed(mouseAreaXZ, planePos, screenPos) onPressed: rootModel.handlePressed(mouseAreaXZ, planePos, screenPos)
onDragged: rootModel.handleDragged(mouseAreaXZ, planePos, screenPos) onDragged: rootModel.handleDragged(mouseAreaXZ, planePos, screenPos)

View File

@@ -161,7 +161,7 @@ Node {
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1)) color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1))
: Qt.rgba(0.5, 0.5, 0.5, 1) : Qt.rgba(0.5, 0.5, 0.5, 1)
rotation: view3D.camera.rotation rotation: view3D.camera.rotation
priority: 1 priority: 10
targetNode: moveGizmo.targetNode targetNode: moveGizmo.targetNode
view3D: moveGizmo.view3D view3D: moveGizmo.view3D

View File

@@ -185,7 +185,7 @@ Node {
height: 120 height: 120
rotation: view3D.camera.rotation rotation: view3D.camera.rotation
grabsMouse: scaleGizmo.targetNode grabsMouse: scaleGizmo.targetNode
priority: 1 priority: 10
active: scaleGizmo.visible active: scaleGizmo.visible
dragHelper: scaleGizmo.dragHelper dragHelper: scaleGizmo.dragHelper

View File

@@ -314,6 +314,7 @@ void NodeInstanceServer::stopRenderTimer()
void NodeInstanceServer::createScene(const CreateSceneCommand &command) void NodeInstanceServer::createScene(const CreateSceneCommand &command)
{ {
setTranslationLanguage(command.language());
initializeView(); initializeView();
Internal::QmlPrivateGate::stopUnifiedTimer(); Internal::QmlPrivateGate::stopUnifiedTimer();
@@ -1329,6 +1330,20 @@ void NodeInstanceServer::loadDummyContextObjectFile(const QFileInfo& qmlFileInfo
refreshBindings(); refreshBindings();
} }
void NodeInstanceServer::setTranslationLanguage(const QString &language)
{
static QPointer<MultiLanguage::Translator> multilanguageTranslator;
if (!MultiLanguage::databaseFilePath().isEmpty()) {
if (!multilanguageLink) {
multilanguageLink = std::make_unique<MultiLanguage::Link>();
multilanguageTranslator = multilanguageLink->translator().release();
QCoreApplication::installTranslator(multilanguageTranslator);
}
if (multilanguageTranslator)
multilanguageTranslator->setLanguage(language);
}
}
void NodeInstanceServer::loadDummyDataFiles(const QString& directory) void NodeInstanceServer::loadDummyDataFiles(const QString& directory)
{ {
QDir dir(directory, "*.qml"); QDir dir(directory, "*.qml");
@@ -1400,16 +1415,7 @@ void NodeInstanceServer::view3DAction(const View3DActionCommand &command)
void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command) void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command)
{ {
static QPointer<MultiLanguage::Translator> multilanguageTranslator; setTranslationLanguage(command.language);
if (!MultiLanguage::databaseFilePath().isEmpty()) {
if (!multilanguageLink) {
multilanguageLink = std::make_unique<MultiLanguage::Link>();
multilanguageTranslator = multilanguageLink->translator().release();
QCoreApplication::installTranslator(multilanguageTranslator);
}
if (multilanguageTranslator)
multilanguageTranslator->setLanguage(command.language);
}
QEvent ev(QEvent::LanguageChange); QEvent ev(QEvent::LanguageChange);
QCoreApplication::sendEvent(QCoreApplication::instance(), &ev); QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);
engine()->retranslate(); engine()->retranslate();

View File

@@ -241,6 +241,7 @@ protected:
virtual void initializeView() = 0; virtual void initializeView() = 0;
virtual void setupScene(const CreateSceneCommand &command) = 0; virtual void setupScene(const CreateSceneCommand &command) = 0;
void setTranslationLanguage(const QString &language);
void loadDummyDataFiles(const QString& directory); void loadDummyDataFiles(const QString& directory);
void loadDummyDataContext(const QString& directory); void loadDummyDataContext(const QString& directory);
void loadDummyDataFile(const QFileInfo& fileInfo); void loadDummyDataFile(const QFileInfo& fileInfo);

View File

@@ -46,6 +46,7 @@ Qt5PreviewNodeInstanceServer::Qt5PreviewNodeInstanceServer(NodeInstanceClientInt
void Qt5PreviewNodeInstanceServer::createScene(const CreateSceneCommand &command) void Qt5PreviewNodeInstanceServer::createScene(const CreateSceneCommand &command)
{ {
setTranslationLanguage(command.language());
initializeView(); initializeView();
setupScene(command); setupScene(command);
startRenderTimer(); startRenderTimer();

View File

@@ -5,9 +5,11 @@ CONFIG += c++11
DEFINES -= QT_CREATOR DEFINES -= QT_CREATOR
# This .pri file contains classes to enable a special multilanguage translator debug service # This .pri file contains classes to enable a special multilanguage translator
MULTILANGUAGE_SUPPORT_PRI=$$(MULTILANGUAGE_SUPPORT_PRI) MULTILANGUAGE_SUPPORT_PRI=$$(MULTILANGUAGE_SUPPORT_PRI)
!isEmpty(MULTILANGUAGE_SUPPORT_PRI) { !isEmpty(MULTILANGUAGE_SUPPORT_PRI) {
exists($$(MULTILANGUAGE_SUPPORT_PRI)): message(including \"$$(MULTILANGUAGE_SUPPORT_PRI)\")
else: error("MULTILANGUAGE_SUPPORT_PRI: \"$$(MULTILANGUAGE_SUPPORT_PRI)\" does not exist.")
include($$(MULTILANGUAGE_SUPPORT_PRI)) include($$(MULTILANGUAGE_SUPPORT_PRI))
DEFINES += MULTILANGUAGE_TRANSLATIONPROVIDER DEFINES += MULTILANGUAGE_TRANSLATIONPROVIDER
} }

View File

@@ -47,11 +47,11 @@ inline QString qmlDebugServices(QmlDebugServicesPreset preset)
case NoQmlDebugServices: case NoQmlDebugServices:
return QString(); return QString();
case QmlDebuggerServices: case QmlDebuggerServices:
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector"); return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector,DebugTranslation");
case QmlProfilerServices: case QmlProfilerServices:
return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages"); return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages,DebugTranslation");
case QmlNativeDebuggerServices: case QmlNativeDebuggerServices:
return QStringLiteral("NativeQmlDebugger"); return QStringLiteral("NativeQmlDebugger,DebugTranslation");
case QmlPreviewServices: case QmlPreviewServices:
return QStringLiteral("QmlPreview,DebugTranslation"); return QStringLiteral("QmlPreview,DebugTranslation");
default: default:

View File

@@ -621,7 +621,6 @@ class UnsupportedTypesByQmlUi : public QStringList
public: public:
UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect", UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect",
"Component", "Component",
"Transition",
"Drawer"}) "Drawer"})
{ {
append(UnsupportedTypesByVisualDesigner()); append(UnsupportedTypesByVisualDesigner());

View File

@@ -251,10 +251,46 @@ sqlite3 *BaseStatement::sqliteDatabaseHandle() const
void BaseStatement::checkForStepError(int resultCode) const void BaseStatement::checkForStepError(int resultCode) const
{ {
switch (resultCode) { switch (resultCode) {
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); case SQLITE_BUSY:
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to "
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); "acquire the database locks!");
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); case SQLITE_ERROR:
throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a "
"constraint violation) has occurred!");
case SQLITE_MISUSE:
throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
case SQLITE_CONSTRAINT:
throwConstraintPreventsModification(
"SqliteStatement::stepStatement: contraint prevent insert or update!");
case SQLITE_TOOBIG:
throwTooBig("SqliteStatement::stepStatement: Some is to bigger than SQLITE_MAX_LENGTH.");
case SQLITE_SCHEMA:
throwSchemaChangeError("SqliteStatement::stepStatement: Schema changed but the statement "
"cannot be recompiled.");
case SQLITE_READONLY:
throwCannotWriteToReadOnlyConnection(
"SqliteStatement::stepStatement: Cannot write to read only connection");
case SQLITE_PROTOCOL:
throwProtocolError(
"SqliteStatement::stepStatement: Something strang with the file locking happened.");
case SQLITE_NOMEM:
throw std::bad_alloc();
case SQLITE_NOLFS:
throwDatabaseExceedsMaximumFileSize(
"SqliteStatement::stepStatement: Database exceeds maximum file size.");
case SQLITE_MISMATCH:
throwDataTypeMismatch(
"SqliteStatement::stepStatement: Most probably you used not an integer for a rowid.");
case SQLITE_LOCKED:
throwConnectionIsLocked("SqliteStatement::stepStatement: Database connection is locked.");
case SQLITE_IOERR:
throwInputOutputError("SqliteStatement::stepStatement: An IO error happened.");
case SQLITE_INTERRUPT:
throwExecutionInterrupted("SqliteStatement::stepStatement: Execution was interrupted.");
case SQLITE_CORRUPT:
throwDatabaseIsCorrupt("SqliteStatement::stepStatement: Database is corrupt.");
case SQLITE_CANTOPEN:
throwCannotOpen("SqliteStatement::stepStatement: Cannot open database or temporary file.");
} }
throwUnknowError("SqliteStatement::stepStatement: unknown error has happened"); throwUnknowError("SqliteStatement::stepStatement: unknown error has happened");
@@ -263,10 +299,17 @@ void BaseStatement::checkForStepError(int resultCode) const
void BaseStatement::checkForResetError(int resultCode) const void BaseStatement::checkForResetError(int resultCode) const
{ {
switch (resultCode) { switch (resultCode) {
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!"); case SQLITE_BUSY:
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!"); throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to "
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!"); "acquire the database locks!");
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!"); case SQLITE_ERROR:
throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a "
"constraint violation) has occurred!");
case SQLITE_MISUSE:
throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
case SQLITE_CONSTRAINT:
throwConstraintPreventsModification(
"SqliteStatement::stepStatement: contraint prevent insert or update!");
} }
throwUnknowError("SqliteStatement::reset: unknown error has happened"); throwUnknowError("SqliteStatement::reset: unknown error has happened");
@@ -278,7 +321,8 @@ void BaseStatement::checkForPrepareError(int resultCode) const
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!"); case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!");
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!"); case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!");
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!"); case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!");
case SQLITE_IOERR: throwIoError("SqliteStatement::prepareStatement: IO error happened!"); case SQLITE_IOERR:
throwInputOutputError("SqliteStatement::prepareStatement: IO error happened!");
} }
throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened"); throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened");
@@ -346,9 +390,9 @@ void BaseStatement::throwStatementIsMisused(const char *whatHasHappened) const
throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
} }
void BaseStatement::throwIoError(const char *whatHasHappened) const void BaseStatement::throwInputOutputError(const char *whatHasHappened) const
{ {
throw IoError(whatHasHappened); throw InputOutputError(whatHasHappened);
} }
void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const
@@ -384,6 +428,56 @@ void BaseStatement::throwBingingTooBig(const char *whatHasHappened) const
throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())); throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
} }
void BaseStatement::throwTooBig(const char *whatHasHappened) const
{
throw TooBig{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwSchemaChangeError(const char *whatHasHappened) const
{
throw SchemeChangeError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const
{
throw CannotWriteToReadOnlyConnection{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwProtocolError(const char *whatHasHappened) const
{
throw ProtocolError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const
{
throw DatabaseExceedsMaximumFileSize{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwDataTypeMismatch(const char *whatHasHappened) const
{
throw DataTypeMismatch{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwConnectionIsLocked(const char *whatHasHappened) const
{
throw ConnectionIsLocked{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
}
void BaseStatement::throwExecutionInterrupted(const char *whatHasHappened) const
{
throw ExecutionInterrupted{whatHasHappened};
}
void BaseStatement::throwDatabaseIsCorrupt(const char *whatHasHappened) const
{
throw DatabaseIsCorrupt{whatHasHappened};
}
void BaseStatement::throwCannotOpen(const char *whatHasHappened) const
{
throw CannotOpen{whatHasHappened};
}
QString BaseStatement::columnName(int column) const QString BaseStatement::columnName(int column) const
{ {
return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column)); return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column));

View File

@@ -109,7 +109,7 @@ public:
[[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const; [[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
[[noreturn]] void throwStatementHasError(const char *whatHasHappened) const; [[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
[[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const; [[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const;
[[noreturn]] void throwIoError(const char *whatHasHappened) const; [[noreturn]] void throwInputOutputError(const char *whatHasHappened) const;
[[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const; [[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const;
[[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const; [[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const;
[[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const; [[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const;
@@ -117,6 +117,16 @@ public:
[[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const; [[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const;
[[noreturn]] void throwUnknowError(const char *whatHasHappened) const; [[noreturn]] void throwUnknowError(const char *whatHasHappened) const;
[[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const; [[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const;
[[noreturn]] void throwTooBig(const char *whatHasHappened) const;
[[noreturn]] void throwSchemaChangeError(const char *whatHasHappened) const;
[[noreturn]] void throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const;
[[noreturn]] void throwProtocolError(const char *whatHasHappened) const;
[[noreturn]] void throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const;
[[noreturn]] void throwDataTypeMismatch(const char *whatHasHappened) const;
[[noreturn]] void throwConnectionIsLocked(const char *whatHasHappened) const;
[[noreturn]] void throwExecutionInterrupted(const char *whatHasHappened) const;
[[noreturn]] void throwDatabaseIsCorrupt(const char *whatHasHappened) const;
[[noreturn]] void throwCannotOpen(const char *whatHasHappened) const;
QString columnName(int column) const; QString columnName(int column) const;

View File

@@ -91,10 +91,10 @@ public:
} }
}; };
class IoError : public Exception class InputOutputError : public Exception
{ {
public: public:
IoError(const char *whatErrorHasHappen) InputOutputError(const char *whatErrorHasHappen)
: Exception(whatErrorHasHappen) : Exception(whatErrorHasHappen)
{ {
} }
@@ -267,6 +267,14 @@ public:
} }
}; };
class TooBig : public Exception
{
public:
TooBig(const char *whatErrorHasHappen, Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class CannotConvert : public Exception class CannotConvert : public Exception
{ {
public: public:
@@ -299,4 +307,84 @@ public:
{} {}
}; };
class SchemeChangeError : public Exception
{
public:
SchemeChangeError(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class CannotWriteToReadOnlyConnection : public Exception
{
public:
CannotWriteToReadOnlyConnection(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class ProtocolError : public Exception
{
public:
ProtocolError(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class DatabaseExceedsMaximumFileSize : public Exception
{
public:
DatabaseExceedsMaximumFileSize(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class DataTypeMismatch : public Exception
{
public:
DataTypeMismatch(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class ConnectionIsLocked : public Exception
{
public:
ConnectionIsLocked(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class ExecutionInterrupted : public Exception
{
public:
ExecutionInterrupted(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class DatabaseIsCorrupt : public Exception
{
public:
DatabaseIsCorrupt(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
class CannotOpen : public Exception
{
public:
CannotOpen(const char *whatErrorHasHappen,
Utils::SmallString &&errorMessage = Utils::SmallString())
: Exception(whatErrorHasHappen, std::move(errorMessage))
{}
};
} // namespace Sqlite } // namespace Sqlite

View File

@@ -454,8 +454,6 @@ AndroidSettingsWidget::AndroidSettingsWidget()
m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_ui.downloadOpenJDKToolButton->setVisible(!HostOsInfo::isLinuxHost());
const QIcon downloadIcon = Icons::ONLINE.icon(); const QIcon downloadIcon = Icons::ONLINE.icon();
m_ui.downloadSDKToolButton->setIcon(downloadIcon); m_ui.downloadSDKToolButton->setIcon(downloadIcon);
m_ui.downloadNDKToolButton->setIcon(downloadIcon); m_ui.downloadNDKToolButton->setIcon(downloadIcon);
@@ -696,7 +694,10 @@ void AndroidSettingsWidget::openNDKDownloadUrl()
void AndroidSettingsWidget::openOpenJDKDownloadUrl() void AndroidSettingsWidget::openOpenJDKDownloadUrl()
{ {
QDesktopServices::openUrl(QUrl::fromUserInput("https://www.oracle.com/java/technologies/javase-jdk8-downloads.html")); if (HostOsInfo::isLinuxHost())
QDesktopServices::openUrl(QUrl::fromUserInput("https://openjdk.java.net/install/"));
else
QDesktopServices::openUrl(QUrl::fromUserInput("https://adoptopenjdk.net/"));
} }
void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent) void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)

View File

@@ -153,12 +153,15 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
} }
return true; return true;
#else #else
#ifndef Q_CC_MSVC #ifdef _MSC_VER
#pragma message( \
"ClangFormat: building dummy plugin due to unmodified Clang, see README.md for more info")
#else
#warning ClangFormat: building dummy plugin due to unmodified Clang, see README.md for more info #warning ClangFormat: building dummy plugin due to unmodified Clang, see README.md for more info
#endif #endif
*errorString = QStringLiteral("Disabling ClangFormat plugin as it has not been built against a modified Clang's libFormat." *errorString = "Disabling ClangFormat plugin as it has not been built against a modified Clang's libFormat."
"For more information see the Qt Creator README at " "For more information see the Qt Creator README at "
"https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/README.md"); "https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/README.md";
return false; return false;
#endif #endif
} }

View File

@@ -38,6 +38,29 @@ if (APPLE)
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
endif() endif()
add_qtc_plugin(assetexporterplugin
CONDITION TARGET QmlDesigner
DEPENDS Core ProjectExplorer QmlDesigner Utils Qt5::Qml
PUBLIC_INCLUDES assetexporterplugin
SOURCES
assetexporterplugin/assetexportdialog.h assetexporterplugin/assetexportdialog.cpp assetexporterplugin/assetexportdialog.ui
assetexporterplugin/assetexporter.h assetexporterplugin/assetexporter.cpp
assetexporterplugin/assetexporterplugin.h assetexporterplugin/assetexporterplugin.cpp
assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp
assetexporterplugin/assetexportpluginconstants.h
assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp
assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp
assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp
assetexporterplugin/parsers/assetnodeparser.h assetexporterplugin/parsers/assetnodeparser.cpp
assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp
assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp
assetexporterplugin/parsers/textnodeparser.h assetexporterplugin/parsers/textnodeparser.cpp
assetexporterplugin/assetexporterplugin.qrc
PLUGIN_PATH ${QmlDesignerPluginInstallPrefix}
SKIP_DEBUG_CMAKE_FILE_CHECK
)
add_qtc_plugin(componentsplugin add_qtc_plugin(componentsplugin
CONDITION TARGET QmlDesigner CONDITION TARGET QmlDesigner
DEPENDS Core QmlDesigner Utils Qt5::Qml DEPENDS Core QmlDesigner Utils Qt5::Qml
@@ -646,6 +669,22 @@ extend_qtc_plugin(QmlDesigner
timelinewidget.cpp timelinewidget.h timelinewidget.cpp timelinewidget.h
) )
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/transitioneditor
SOURCES
transitioneditorview.cpp transitioneditorview.h
transitioneditorwidget.cpp transitioneditorwidget.h
transitioneditortoolbar.cpp transitioneditortoolbar.h
transitioneditorgraphicsscene.cpp transitioneditorgraphicsscene.h
transitioneditorgraphicslayout.cpp transitioneditorgraphicslayout.h
transitioneditorsectionitem.cpp transitioneditorsectionitem.h
transitioneditorpropertyitem.cpp transitioneditorpropertyitem.h
transitioneditorsettingsdialog.cpp transitioneditorsettingsdialog.h
transitioneditorsettingsdialog.ui
transitionform.cpp transitionform.h
transitioneditor.qrc
)
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/curveeditor SOURCES_PREFIX components/curveeditor
SOURCES SOURCES

View File

@@ -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 "assetexportdialog.h"
#include "ui_assetexportdialog.h"
#include "assetexportpluginconstants.h"
#include "filepathmodel.h"
#include "coreplugin/fileutils.h"
#include "coreplugin/icore.h"
#include "projectexplorer/task.h"
#include "projectexplorer/taskhub.h"
#include "utils/fileutils.h"
#include "utils/outputformatter.h"
#include <QPushButton>
#include <QListView>
#include <QPlainTextEdit>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QScrollBar>
#include <algorithm>
#include <cmath>
namespace {
static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str,
Utils::OutputFormat format) {
if (!formatter)
return;
QPlainTextEdit *edit = formatter->plainTextEdit();
QScrollBar *scroll = edit->verticalScrollBar();
bool isAtBottom = scroll && scroll->value() == scroll->maximum();
QString msg = str + "\n";
formatter->appendMessage(msg, format);
if (isAtBottom)
scroll->setValue(scroll->maximum());
}
}
using namespace ProjectExplorer;
namespace QmlDesigner {
AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
AssetExporter &assetExporter, FilePathModel &model,
QWidget *parent) :
QDialog(parent),
m_assetExporter(assetExporter),
m_filePathModel(model),
m_ui(new Ui::AssetExportDialog),
m_filesView(new QListView),
m_exportLogs(new QPlainTextEdit),
m_outputFormatter(new Utils::OutputFormatter())
{
m_ui->setupUi(this);
m_ui->exportPath->setFileName(exportPath);
m_ui->exportPath->setPromptDialogTitle(tr("Choose Export Path"));
m_ui->exportPath->lineEdit()->setReadOnly(true);
m_ui->exportPath->addButton(tr("Open"), this, [this]() {
Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_ui->exportPath->path());
});
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
m_ui->stackedWidget->addWidget(m_filesView);
m_filesView->setModel(&m_filePathModel);
m_exportLogs->setReadOnly(true);
m_outputFormatter->setPlainTextEdit(m_exportLogs);
m_ui->stackedWidget->addWidget(m_exportLogs);
switchView(false);
connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() {
m_assetExporter.cancel();
});
m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
m_exportBtn->setEnabled(false);
connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport);
connect(&m_filePathModel, &FilePathModel::modelReset, this, [this]() {
m_ui->exportProgress->setRange(0, 1000);
m_ui->exportProgress->setValue(0);
m_exportBtn->setEnabled(true);
});
connect(m_ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() {
close();
});
m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false);
connect(&m_assetExporter, &AssetExporter::stateChanged,
this, &AssetExportDialog::onExportStateChanged);
connect(&m_assetExporter, &AssetExporter::exportProgressChanged,
this, &AssetExportDialog::updateExportProgress);
connect(TaskHub::instance(), &TaskHub::taskAdded, this, &AssetExportDialog::onTaskAdded);
m_ui->exportProgress->setRange(0,0);
}
AssetExportDialog::~AssetExportDialog()
{
m_assetExporter.cancel();
}
void AssetExportDialog::onExport()
{
switchView(true);
updateExportProgress(0.0);
TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT);
m_exportLogs->clear();
m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName());
}
void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState)
{
switch (newState) {
case AssetExporter::ParsingState::ExportingDone:
m_exportBtn->setVisible(false);
m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true);
break;
default:
break;
}
m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::ExportingDone);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy());
}
void AssetExportDialog::updateExportProgress(double value)
{
value = std::max(0.0, std::min(1.0, value));
m_ui->exportProgress->setValue(std::round(value * 1000));
}
void AssetExportDialog::switchView(bool showExportView)
{
if (showExportView)
m_ui->stackedWidget->setCurrentWidget(m_exportLogs);
else
m_ui->stackedWidget->setCurrentWidget(m_filesView);
}
void AssetExportDialog::onTaskAdded(const ProjectExplorer::Task &task)
{
Utils::OutputFormat format = Utils::NormalMessageFormat;
if (task.category == Constants::TASK_CATEGORY_ASSET_EXPORT) {
switch (task.type) {
case ProjectExplorer::Task::Error:
format = Utils::StdErrFormat;
break;
case ProjectExplorer::Task::Warning:
format = Utils::StdOutFormat;
break;
default:
format = Utils::NormalMessageFormat;
}
addFormattedMessage(m_outputFormatter, task.description(), format);
}
}
}

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "assetexporter.h"
#include <QDialog>
#include <QStringListModel>
#include "utils/fileutils.h"
#include <memory>
QT_BEGIN_NAMESPACE
class QPushButton;
class QListView;
class QPlainTextEdit;
QT_END_NAMESPACE
namespace Ui {
class AssetExportDialog;
}
namespace Utils {
class OutputFormatter;
}
namespace ProjectExplorer {
class Task;
}
namespace QmlDesigner {
class FilePathModel;
class AssetExportDialog : public QDialog
{
Q_OBJECT
public:
explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter,
FilePathModel& model, QWidget *parent = nullptr);
~AssetExportDialog();
private:
void onExport();
void onExportStateChanged(AssetExporter::ParsingState newState);
void updateExportProgress(double value);
void switchView(bool showExportView);
void onTaskAdded(const ProjectExplorer::Task &task);
private:
AssetExporter &m_assetExporter;
FilePathModel &m_filePathModel;
std::unique_ptr<Ui::AssetExportDialog> m_ui;
QPushButton *m_exportBtn = nullptr;
QListView *m_filesView = nullptr;
QPlainTextEdit *m_exportLogs = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
};
}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AssetExportDialog</class>
<widget class="QDialog" name="AssetExportDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>768</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Export QML</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="Utils::PathChooser" name="exportPath" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Export path:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QStackedWidget" name="stackedWidget"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QProgressBar" name="exportProgress">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,370 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "assetexporter.h"
#include "componentexporter.h"
#include "exportnotification.h"
#include "assetexportpluginconstants.h"
#include "rewriterview.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
#include "utils/qtcassert.h"
#include "utils/runextensions.h"
#include "variantproperty.h"
#include <QCryptographicHash>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QLoggingCategory>
#include <QWaitCondition>
#include <random>
#include <queue>
using namespace ProjectExplorer;
using namespace std;
namespace {
bool makeParentPath(const Utils::FilePath &path)
{
QDir d;
return d.mkpath(path.toFileInfo().absolutePath());
}
QByteArray generateHash(const QString &token) {
static uint counter = 0;
std::mt19937 gen(std::random_device().operator()());
std::uniform_int_distribution<> distribution(1, 99999);
QByteArray data = QString("%1%2%3").arg(token).arg(++counter).arg(distribution(gen)).toLatin1();
return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex();
}
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg)
}
namespace QmlDesigner {
class AssetDumper
{
public:
AssetDumper();
~AssetDumper();
void dumpAsset(const QPixmap &p, const Utils::FilePath &path);
/* Keeps on dumping until all assets are dumped, then quits */
void quitDumper();
/* Aborts dumping */
void abortDumper();
private:
void addAsset(const QPixmap &p, const Utils::FilePath &path);
void doDumping(QFutureInterface<void> &fi);
void savePixmap(const QPixmap &p, Utils::FilePath &path) const;
QFuture<void> m_dumpFuture;
QMutex m_queueMutex;
QWaitCondition m_queueCondition;
std::queue<std::pair<QPixmap, Utils::FilePath>> m_assets;
std::atomic<bool> m_quitDumper;
};
AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) :
QObject(parent),
m_currentState(*this),
m_project(project),
m_view(view)
{
connect(m_view, &AssetExporterView::loadingFinished, this, &AssetExporter::onQmlFileLoaded);
connect(m_view, &AssetExporterView::loadingError, this, &AssetExporter::notifyLoadError);
}
AssetExporter::~AssetExporter()
{
cancel();
}
void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
bool exportAssets)
{
ExportNotification::addInfo(tr("Exporting metadata at %1. Export assets: ")
.arg(exportPath.toUserOutput())
.arg(exportAssets? tr("Yes") : tr("No")));
// TODO Asset export
notifyProgress(0.0);
Q_UNUSED(exportAssets);
m_exportFiles = qmlFiles;
m_components = QJsonArray();
m_exportPath = exportPath;
m_currentState.change(ParsingState::Parsing);
triggerLoadNextFile();
m_assetDumper = make_unique<AssetDumper>();
}
void AssetExporter::cancel()
{
// TODO Cancel export
m_assetDumper.reset();
}
bool AssetExporter::isBusy() const
{
return m_currentState == AssetExporter::ParsingState::Parsing ||
m_currentState == AssetExporter::ParsingState::ExportingAssets ||
m_currentState == AssetExporter::ParsingState::WritingJson;
}
Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node)
{
// TODO: Use this hash as UUID and add to the node.
QByteArray hash = addNodeUUID(node.modelNode());
Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png")
.arg(QString::fromLatin1(hash)));
m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath);
return assetPath;
}
void AssetExporter::exportComponent(const ModelNode &rootNode)
{
qCDebug(loggerInfo) << "Exporting component" << rootNode.id();
Component exporter(*this, rootNode);
exporter.exportComponent();
m_components.append(exporter.json());
}
void AssetExporter::notifyLoadError(AssetExporterView::LoadState state)
{
QString errorStr = tr("Unknown error.");
switch (state) {
case AssetExporterView::LoadState::Exausted:
errorStr = tr("Loading file is taking too long.");
break;
case AssetExporterView::LoadState::QmlErrorState:
errorStr = tr("Cannot parse. QML file has errors.");
break;
default:
return;
}
qCDebug(loggerError) << "QML load error:" << errorStr;
ExportNotification::addError(tr("Loading QML failed. %1").arg(errorStr));
}
void AssetExporter::notifyProgress(double value) const
{
emit exportProgressChanged(value);
}
void AssetExporter::onQmlFileLoaded()
{
QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return);
qCDebug(loggerInfo) << "Qml file load done" << m_view->model()->fileUrl();
exportComponent(m_view->rootModelNode());
QString error;
if (!m_view->saveQmlFile(&error)) {
ExportNotification::addError(tr("Error saving QML file. %1")
.arg(error.isEmpty()? tr("Unknown") : error));
}
triggerLoadNextFile();
}
QByteArray AssetExporter::addNodeUUID(ModelNode node)
{
QByteArray uuid = node.auxiliaryData(Constants::UuidTag).toByteArray();
qDebug() << node.id() << "UUID" << uuid;
if (uuid.isEmpty()) {
// Assign a new hash.
do {
uuid = generateHash(node.id());
} while (m_usedHashes.contains(uuid));
m_usedHashes.insert(uuid);
node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid));
node.model()->rewriterView()->writeAuxiliaryData();
}
return uuid;
}
void AssetExporter::triggerLoadNextFile()
{
QTimer::singleShot(0, this, &AssetExporter::loadNextFile);
}
void AssetExporter::loadNextFile()
{
if (m_exportFiles.isEmpty()) {
notifyProgress(0.8);
m_currentState.change(ParsingState::ParsingFinished);
writeMetadata();
return;
}
// Load the next pending file.
const Utils::FilePath file = m_exportFiles.takeFirst();
ExportNotification::addInfo(tr("Exporting file %1.").arg(file.toUserOutput()));
qCDebug(loggerInfo) << "Loading next file" << file;
m_view->loadQmlFile(file);
}
void AssetExporter::writeMetadata() const
{
Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata");
ExportNotification::addInfo(tr("Writing metadata to file %1.").
arg(metadataPath.toUserOutput()));
makeParentPath(metadataPath);
m_currentState.change(ParsingState::WritingJson);
QJsonObject jsonRoot; // TODO: Write plugin info to root
jsonRoot.insert("artboards", m_components);
QJsonDocument doc(jsonRoot);
if (doc.isNull() || doc.isEmpty()) {
ExportNotification::addError(tr("Empty JSON document."));
} else {
Utils::FileSaver saver(metadataPath.toString(), QIODevice::Text);
saver.write(doc.toJson(QJsonDocument::Indented));
if (!saver.finalize()) {
ExportNotification::addError(tr("Writing metadata failed. %1").
arg(saver.errorString()));
}
}
notifyProgress(1.0);
ExportNotification::addInfo(tr("Export finished."));
m_assetDumper->quitDumper();
m_currentState.change(ParsingState::ExportingDone);
}
AssetExporter::State::State(AssetExporter &exporter) :
m_assetExporter(exporter)
{
}
void AssetExporter::State::change(const ParsingState &state)
{
qCDebug(loggerInfo()) << "Assetimporter State change: Old: " << m_state << "New: " << state;
if (m_state != state) {
m_state = state;
m_assetExporter.stateChanged(m_state);
}
}
QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s)
{
os << static_cast<std::underlying_type<QmlDesigner::AssetExporter::ParsingState>::type>(s);
return os;
}
AssetDumper::AssetDumper():
m_quitDumper(false)
{
m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this);
}
AssetDumper::~AssetDumper()
{
abortDumper();
}
void AssetDumper::dumpAsset(const QPixmap &p, const Utils::FilePath &path)
{
addAsset(p, path);
}
void AssetDumper::quitDumper()
{
m_quitDumper = true;
m_queueCondition.wakeAll();
if (!m_dumpFuture.isFinished())
m_dumpFuture.waitForFinished();
}
void AssetDumper::abortDumper()
{
if (!m_dumpFuture.isFinished()) {
m_dumpFuture.cancel();
m_queueCondition.wakeAll();
m_dumpFuture.waitForFinished();
}
}
void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path)
{
QMutexLocker locker(&m_queueMutex);
qDebug() << "Save Asset:" << path;
m_assets.push({p, path});
}
void AssetDumper::doDumping(QFutureInterface<void> &fi)
{
auto haveAsset = [this] (std::pair<QPixmap, Utils::FilePath> *asset) {
QMutexLocker locker(&m_queueMutex);
if (m_assets.empty())
return false;
*asset = m_assets.front();
m_assets.pop();
return true;
};
forever {
std::pair<QPixmap, Utils::FilePath> asset;
if (haveAsset(&asset)) {
if (fi.isCanceled())
break;
savePixmap(asset.first, asset.second);
} else {
if (m_quitDumper)
break;
QMutexLocker locker(&m_queueMutex);
m_queueCondition.wait(&m_queueMutex);
}
if (fi.isCanceled())
break;
}
fi.reportFinished();
}
void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const
{
if (p.isNull()) {
qCDebug(loggerWarn) << "Dumping null pixmap" << path;
return;
}
if (!makeParentPath(path)) {
ExportNotification::addError(AssetExporter::tr("Error creating asset directory. %1")
.arg(path.fileName()));
return;
}
if (!p.save(path.toString())) {
ExportNotification::addError(AssetExporter::tr("Error saving asset. %1")
.arg(path.fileName()));
}
}
}

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "assetexporterview.h"
#include "utils/fileutils.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QSet>
#include <memory>
QT_BEGIN_NAMESPACE
class QJsonArray;
QT_END_NAMESPACE
namespace ProjectExplorer {
class Project;
}
namespace QmlDesigner {
class AssetDumper;
class AssetExporter : public QObject
{
Q_OBJECT
public:
enum class ParsingState {
Idle = 0,
Parsing,
ParsingFinished,
ExportingAssets,
ExportingAssetsFinished,
WritingJson,
ExportingDone
};
AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project,
QObject *parent = nullptr);
~AssetExporter();
void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
bool exportAssets = false);
void cancel();
bool isBusy() const;
Utils::FilePath exportAsset(const QmlObjectNode& node);
signals:
void stateChanged(ParsingState);
void exportProgressChanged(double) const;
private:
ParsingState currentState() const { return m_currentState.m_state; }
void exportComponent(const ModelNode &rootNode);
void writeMetadata() const;
void notifyLoadError(AssetExporterView::LoadState state);
void notifyProgress(double value) const;
void triggerLoadNextFile();
void loadNextFile();
void onQmlFileLoaded();
QByteArray addNodeUUID(ModelNode node);
private:
mutable class State {
public:
State(AssetExporter&);
void change(const ParsingState &state);
operator ParsingState() const { return m_state; }
AssetExporter &m_assetExporter;
ParsingState m_state = ParsingState::Idle;
} m_currentState;
ProjectExplorer::Project *m_project = nullptr;
AssetExporterView *m_view = nullptr;
Utils::FilePaths m_exportFiles;
Utils::FilePath m_exportPath;
QJsonArray m_components;
QSet<QByteArray> m_usedHashes;
std::unique_ptr<AssetDumper> m_assetDumper;
};
QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s);
} // namespace QmlDesigner

View File

@@ -0,0 +1,130 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "assetexporterplugin.h"
#include "assetexportpluginconstants.h"
#include "assetexportdialog.h"
#include "assetexporter.h"
#include "assetexporterview.h"
#include "filepathmodel.h"
#include "componentexporter.h"
#include "parsers/modelitemnodeparser.h"
#include "parsers/textnodeparser.h"
#include "parsers/assetnodeparser.h"
#include "coreplugin/actionmanager/actionmanager.h"
#include "coreplugin/actionmanager/actioncontainer.h"
#include "coreplugin/documentmanager.h"
#include "qmldesigner/qmldesignerplugin.h"
#include "projectexplorer/projectexplorerconstants.h"
#include "projectexplorer/session.h"
#include "projectexplorer/project.h"
#include "projectexplorer/session.h"
#include "projectexplorer/taskhub.h"
#include "extensionsystem/pluginmanager.h"
#include "extensionsystem/pluginspec.h"
#include "utils/algorithm.h"
#include <QCoreApplication>
#include <QAction>
#include <QLoggingCategory>
namespace QmlDesigner {
AssetExporterPlugin::AssetExporterPlugin() :
m_view(new AssetExporterView)
{
ProjectExplorer::TaskHub::addCategory( Constants::TASK_CATEGORY_ASSET_EXPORT,
tr("Asset Export"), false);
auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
auto &viewManager = designerPlugin->viewManager();
viewManager.registerViewTakingOwnership(m_view);
// Add parsers templates for factory instantiation.
Component::addNodeParser<ItemNodeParser>();
Component::addNodeParser<TextNodeParser>();
Component::addNodeParser<AssetNodeParser>();
// Instantiate actions created by the plugin.
addActions();
connect(ProjectExplorer::SessionManager::instance(),
&ProjectExplorer::SessionManager::startupProjectChanged,
this, &AssetExporterPlugin::updateActions);
updateActions();
}
QString AssetExporterPlugin::pluginName() const
{
return QLatin1String("AssetExporterPlugin");
}
void AssetExporterPlugin::onExport()
{
auto startupProject = ProjectExplorer::SessionManager::startupProject();
if (!startupProject)
return;
FilePathModel model(startupProject);
QString exportDirName = startupProject->displayName() + "_export";
auto exportDir = startupProject->projectFilePath().parentDir().pathAppended(exportDirName);
AssetExporter assetExporter(m_view, startupProject);
AssetExportDialog assetExporterDialog(exportDir, assetExporter, model);
assetExporterDialog.exec();
}
void AssetExporterPlugin::addActions()
{
auto exportAction = new QAction(tr("Export QML"));
exportAction->setToolTip(tr("Export QML code of the current project."));
connect(exportAction, &QAction::triggered, this, &AssetExporterPlugin::onExport);
Core::Command *cmd = Core::ActionManager::registerAction(exportAction, Constants::EXPORT_QML);
// Add action to build menu
Core::ActionContainer *buildMenu =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
}
void AssetExporterPlugin::updateActions()
{
auto project = ProjectExplorer::SessionManager::startupProject();
QAction* const exportAction = Core::ActionManager::command(Constants::EXPORT_QML)->action();
exportAction->setEnabled(project && !project->needsConfiguration());
}
QString AssetExporterPlugin::metaInfo() const
{
return QLatin1String(":/assetexporterplugin/assetexporterplugin.metainfo");
}
} //QmlDesigner

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <iwidgetplugin.h>
namespace QmlDesigner {
class AssetExporter;
class AssetExporterView;
class AssetExporterPlugin : public QObject, QmlDesigner::IWidgetPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QmlDesignerPlugin" FILE "assetexporterplugin.json")
Q_DISABLE_COPY(AssetExporterPlugin)
Q_INTERFACES(QmlDesigner::IWidgetPlugin)
public:
AssetExporterPlugin();
QString metaInfo() const;
QString pluginName() const;
private:
void onExport();
void addActions();
void updateActions();
AssetExporterView *m_view = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,6 @@
{
"Vendor" : "The Qt Company Ltd",
"Category" : "Qt Quick",
"Description" : "Plugin for exporting assets and QML from QmlDesigner",
"Url" : "http://www.qt.io"
}

View File

@@ -0,0 +1,2 @@
MetaInfo {
}

View File

@@ -0,0 +1,37 @@
QT *= qml quick core widgets
VPATH += $$PWD
RESOURCES += assetexporterplugin.qrc
INCLUDEPATH += ./
HEADERS += \
assetexportdialog.h \
assetexporter.h \
assetexporterplugin.h \
assetexporterview.h \
assetexportpluginconstants.h \
componentexporter.h \
exportnotification.h \
filepathmodel.h \
parsers/assetnodeparser.h \
parsers/modelitemnodeparser.h \
parsers/modelnodeparser.h \
parsers/textnodeparser.h
SOURCES += \
assetexportdialog.cpp \
assetexporter.cpp \
assetexporterplugin.cpp \
assetexporterview.cpp \
componentexporter.cpp \
exportnotification.cpp \
filepathmodel.cpp \
parsers/assetnodeparser.cpp \
parsers/modelitemnodeparser.cpp \
parsers/modelnodeparser.cpp \
parsers/textnodeparser.cpp
FORMS += \
assetexportdialog.ui

View File

@@ -0,0 +1,17 @@
include (../../../../qtcreator.pri)
include (../plugindestdir.pri)
include (../designercore/iwidgetplugin.pri)
include (../qmldesigner_dependencies.pri)
include (assetexporterplugin.pri)
LIBS += -L$$IDE_PLUGIN_PATH
LIBS += -l$$qtLibraryName(QmlDesigner)
LIBS += -l$$qtLibraryName(ExtensionSystem)
LIBS += -l$$qtLibraryName(Core)
LIBS += -l$$qtLibraryName(ProjectExplorer)
LIBS += -l$$qtLibraryName(Utils)
TARGET = assetexporterplugin
TEMPLATE = lib
CONFIG += plugin

View File

@@ -0,0 +1,59 @@
import qbs
QtcProduct {
name: "assetexporterplugin"
type: ["dynamiclibrary"]
installDir: qtc.ide_plugin_path + '/' + installDirName
property string installDirName: qbs.targetOS.contains("macos") ? "QmlDesigner" : "qmldesigner"
Depends { name: "Core" }
Depends { name: "ProjectExplorer" }
Depends { name: "QmlDesigner" }
Depends { name: "Utils" }
cpp.includePaths: base.concat([
"./",
"../designercore/include",
"../../../../share/qtcreator/qml/qmlpuppet/interfaces",
"../../../../share/qtcreator/qml/qmlpuppet/types"
])
Properties {
condition: qbs.targetOS.contains("unix")
cpp.internalVersion: ""
}
Group {
name: "plugin metadata"
files: ["assetexporterplugin.json"]
fileTags: ["qt_plugin_metadata"]
}
files: [
"assetexportdialog.cpp",
"assetexportdialog.h",
"assetexportdialog.ui",
"assetexporter.cpp",
"assetexporter.h",
"assetexporterplugin.cpp",
"assetexporterplugin.h",
"assetexporterplugin.qrc",
"assetexporterview.cpp",
"assetexporterview.h",
"assetexportpluginconstants.h",
"componentexporter.cpp",
"componentexporter.h",
"exportnotification.cpp",
"exportnotification.h",
"filepathmodel.cpp",
"filepathmodel.h",
"parsers/assetnodeparser.cpp",
"parsers/assetnodeparser.h",
"parsers/modelitemnodeparser.cpp",
"parsers/modelitemnodeparser.h",
"parsers/modelnodeparser.cpp",
"parsers/modelnodeparser.h",
"parsers/textnodeparser.cpp",
"parsers/textnodeparser.h"
]
}

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/assetexporterplugin">
<file>assetexporterplugin.metainfo</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,161 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "assetexporterview.h"
#include "qmlitemnode.h"
#include "rewriterview.h"
#include "coreplugin/editormanager/editormanager.h"
#include "coreplugin/editormanager/ieditor.h"
#include "coreplugin/modemanager.h"
#include "coreplugin/coreconstants.h"
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.view", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.view", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.view", QtCriticalMsg)
static const int RetryIntervalMs = 500;
static const int MinRetry = 2;
}
namespace QmlDesigner {
AssetExporterView::AssetExporterView(QObject *parent) : AbstractView(parent),
m_timer(this)
{
m_timer.setInterval(RetryIntervalMs);
// We periodically check if file is loaded.
connect(&m_timer, &QTimer::timeout, this, &AssetExporterView::handleTimerTimeout);
}
bool AssetExporterView::loadQmlFile(const Utils::FilePath &path, uint timeoutSecs)
{
qCDebug(loggerInfo) << "Load file" << path;
if (loadingState() == LoadState::Busy)
return false;
setState(LoadState::Busy);
m_retryCount = std::max(MinRetry, static_cast<int>((timeoutSecs * 1000) / RetryIntervalMs));
m_currentEditor = Core::EditorManager::openEditor(path.toString(), Core::Id(),
Core::EditorManager::DoNotMakeVisible);
Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN);
Core::ModeManager::setFocusToCurrentMode();
m_timer.start();
return true;
}
bool AssetExporterView::saveQmlFile(QString *error) const
{
if (!m_currentEditor) {
qCDebug(loggerWarn) << "Saving QML file failed. No editor.";
return false;
}
return m_currentEditor->document()->save(error);
}
void AssetExporterView::modelAttached(Model *model)
{
if (model->rewriterView() && model->rewriterView()->inErrorState())
setState(LoadState::QmlErrorState);
AbstractView::modelAttached(model);
}
void AssetExporterView::
instanceInformationsChanged(const QMultiHash<ModelNode, InformationName> &informationChangeHash)
{
if (inErrorState() || loadingState() == LoadState::Loaded)
return; // Already reached error or connected state.
// We expect correct dimensions are available if the rootnode's
// information change message is received.
const auto nodes = informationChangeHash.keys();
bool hasRootNode = std::any_of(nodes.begin(), nodes.end(), [](const ModelNode &n) {
return n.isRootNode();
});
if (hasRootNode)
handleMaybeDone();
}
void AssetExporterView::instancesPreviewImageChanged(const QVector<ModelNode> &nodeList)
{
Q_UNUSED(nodeList);
emit previewChanged();
}
bool AssetExporterView::inErrorState() const
{
return m_state == LoadState::Exausted || m_state == LoadState::QmlErrorState;
}
bool AssetExporterView::isLoaded() const
{
return isAttached() && QmlItemNode(rootModelNode()).isValid();
}
void AssetExporterView::setState(AssetExporterView::LoadState state)
{
if (state != m_state) {
m_state = state;
qCDebug(loggerInfo) << "Loading state changed" << m_state;
if (inErrorState() || m_state == LoadState::Loaded) {
m_timer.stop();
// TODO: Send the loaded signal with a delay. The assumption that model attached and a
// valid root object is enough to declare a QML file is ready is incorrect. A ideal
// solution would be that the puppet notifies file ready signal.
if (m_state == LoadState::Loaded)
QTimer::singleShot(2000, this, &AssetExporterView::loadingFinished);
else
emit loadingError(m_state);
}
}
}
void AssetExporterView::handleMaybeDone()
{
if (isLoaded())
setState(LoadState::Loaded);
}
void AssetExporterView::handleTimerTimeout()
{
if (!inErrorState() && loadingState() != LoadState::Loaded)
handleMaybeDone();
if (--m_retryCount < 0)
setState(LoadState::Exausted);
}
}
QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s)
{
os << static_cast<std::underlying_type<QmlDesigner::AssetExporterView::LoadState>::type>(s);
return os;
}

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "abstractview.h"
#include "utils/fileutils.h"
#include <QObject>
#include <QTimer>
#include <memory>
namespace Core {
class IEditor;
}
namespace QmlDesigner {
class AssetExporterView : public AbstractView
{
Q_OBJECT
public:
enum class LoadState {
Idle = 1,
Busy,
Exausted,
QmlErrorState,
Loaded
};
AssetExporterView(QObject *parent = nullptr);
bool loadQmlFile(const Utils::FilePath &path, uint timeoutSecs = 10);
bool saveQmlFile(QString *error) const;
void modelAttached(Model *model) override;
void instanceInformationsChanged(const QMultiHash<ModelNode, InformationName> &informationChangeHash) override;
void instancesPreviewImageChanged(const QVector<ModelNode> &nodeList) override;
LoadState loadingState() const { return m_state; }
bool inErrorState() const;
signals:
void loadingFinished();
void loadingError(LoadState);
void previewChanged();
private:
bool isLoaded() const;
void setState(LoadState state);
void handleMaybeDone();
void handleTimerTimeout();
Core::IEditor *m_currentEditor = nullptr;
QTimer m_timer;
int m_retryCount = 0;
LoadState m_state = LoadState::Idle;
bool m_waitForPuppet = false;
};
}
QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s);

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
namespace QmlDesigner {
namespace Constants {
const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml";
const char TASK_CATEGORY_ASSET_EXPORT[] = "AssetExporter.Export";
const char UuidAuxTag[] = "uuid";
//***************************************************************************
// Metadata tags
//***************************************************************************
// Plugin info tags
const char PluginInfoTag[] = "pluginInfo";
const char MetadataVersionTag[] = "metadataVersion";
const char DocumentInfoTag[] = "documentInfo";
const char DocumentNameTag[] = "name";
// Layer data tags
const char ArtboardListTag[] = "artboards";
const char XPosTag[] = "x";
const char YPosTag[] = "y";
const char WidthTag[] = "width";
const char HeightTag[] = "height";
const char QmlIdTag[] = "qmlId";
const char ExportTypeTag[] = "exportType";
const char QmlPropertiesTag[] = "qmlProperties";
const char ImportsTag[] = "extraImports";
const char UuidTag[] = "uuid";
const char ClipTag[] = "clip";
const char AssetDataTag[] = "assetData";
const char AssetPathTag[] = "assetPath";
const char AssetBoundsTag[] = "assetBounds";
const char OpacityTag[] = "opacity";
const char TextDetailsTag[] = "textDetails";
const char FontFamilyTag[] = "fontFamily";
const char FontSizeTag[] = "fontSize";
const char FontStyleTag[] = "fontStyle";
const char LetterSpacingTag[] = "kerning";
const char TextColorTag[] = "textColor";
const char TextContentTag[] = "contents";
const char IsMultilineTag[] = "multiline";
const char LineHeightTag[] = "lineHeight";
const char HAlignTag[] = "horizontalAlignment";
const char VAlignTag[] = "verticalAlignment";
}
}

View File

@@ -0,0 +1,119 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "componentexporter.h"
#include "parsers/modelnodeparser.h"
#include "model.h"
#include "nodeabstractproperty.h"
#include "rewriterview.h"
#include "utils/qtcassert.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg)
static void populateLineage(const QmlDesigner::ModelNode &node, QByteArrayList &lineage)
{
if (!node.isValid() || node.type().isEmpty())
return;
lineage.append(node.type());
if (node.hasParentProperty())
populateLineage(node.parentProperty().parentModelNode(), lineage);
}
}
namespace QmlDesigner {
std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> Component::m_readers;
Component::Component(AssetExporter &exporter, const ModelNode &rootNode):
m_exporter(exporter),
m_rootNode(rootNode)
{
}
QJsonObject Component::json() const
{
return m_json;
}
AssetExporter &Component::exporter()
{
return m_exporter;
}
void Component::exportComponent()
{
QTC_ASSERT(m_rootNode.isValid(), return);
m_json = nodeToJson(m_rootNode);
}
ModelNodeParser *Component::createNodeParser(const ModelNode &node) const
{
QByteArrayList lineage;
populateLineage(node, lineage);
std::unique_ptr<ModelNodeParser> reader;
for (auto &parserCreator: m_readers) {
std::unique_ptr<ModelNodeParser> r(parserCreator->instance(lineage, node));
if (r->isExportable()) {
if (reader) {
if (reader->priority() < r->priority())
reader = std::move(r);
} else {
reader = std::move(r);
}
}
}
if (!reader)
qCDebug(loggerInfo()) << "No parser for node" << node;
return reader.release();
}
QJsonObject Component::nodeToJson(const ModelNode &node)
{
QJsonObject jsonObject;
std::unique_ptr<ModelNodeParser> parser(createNodeParser(node));
if (parser)
jsonObject = parser->json(*this);
QJsonArray children;
for (const ModelNode &childnode : node.directSubModelNodes())
children.append(nodeToJson(childnode));
if (!children.isEmpty())
jsonObject.insert("children", children);
return jsonObject;
}
} // namespace QmlDesigner

View File

@@ -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 <QJsonObject>
#include <QByteArrayList>
#include <memory>
#include "utils/qtcassert.h"
QT_BEGIN_NAMESPACE
class QJsonArray;
QT_END_NAMESPACE
namespace QmlDesigner {
class AssetExporter;
class ModelNode;
class Component;
class ModelNodeParser;
namespace Internal {
class NodeParserCreatorBase
{
public:
virtual ~NodeParserCreatorBase() {}
protected:
virtual ModelNodeParser *instance(const QByteArrayList &, const ModelNode &) const = 0;
friend class QmlDesigner::Component;
};
template<class T>
class NodeParserCreator : public NodeParserCreatorBase
{
public:
NodeParserCreator() = default;
~NodeParserCreator() = default;
protected:
ModelNodeParser *instance(const QByteArrayList &lineage, const ModelNode &node) const {
return new T(lineage, node);
}
};
} //Internal
class Component
{
public:
Component(AssetExporter& exporter, const ModelNode &rootNode);
void exportComponent();
QJsonObject json() const;
AssetExporter &exporter();
template<typename T> static void addNodeParser()
{
QTC_ASSERT((std::is_base_of<ModelNodeParser, T>::value), return);
m_readers.push_back(std::make_unique<Internal::NodeParserCreator<T>>());
}
private:
ModelNodeParser* createNodeParser(const ModelNode &node) const;
QJsonObject nodeToJson(const ModelNode &node);
private:
AssetExporter& m_exporter;
const ModelNode &m_rootNode;
QJsonObject m_json;
static std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> m_readers;
};
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd
** All rights reserved.
** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
**
** This file is part of the Qt Asset Importer module.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** If you have questions regarding the use of this file, please use
** contact form at http://www.qt.io/contact-us
**
******************************************************************************/
#include "exportnotification.h"
#include "assetexportpluginconstants.h"
#include "projectexplorer/taskhub.h"
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(loggerDebug, "qtc.designer.assetExportPlugin.exportNotification", QtDebugMsg)
}
using namespace ProjectExplorer;
namespace {
static void addTask(Task::TaskType type, const QString &desc)
{
qCDebug(loggerDebug) << desc;
Task task(type, desc, {}, -1, QmlDesigner::Constants::TASK_CATEGORY_ASSET_EXPORT);
TaskHub::addTask(task);
}
}
namespace QmlDesigner {
void ExportNotification::addError(const QString &errMsg)
{
addTask(Task::Error, errMsg);
}
void ExportNotification::addWarning(const QString &warningMsg)
{
addTask(Task::Warning, warningMsg);
}
void ExportNotification::addInfo(const QString &infoMsg)
{
addTask(Task::Unknown, infoMsg);
}
} // QmlDesigner

View File

@@ -0,0 +1,33 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd
** All rights reserved.
** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
**
** This file is part of the Qt Asset Importer module.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** If you have questions regarding the use of this file, please use
** contact form at http://www.qt.io/contact-us
**
******************************************************************************/
#pragma once
#include <QString>
namespace QmlDesigner {
class ExportNotification
{
public:
static void addError(const QString &errMsg);
static void addWarning(const QString &warningMsg);
static void addInfo(const QString &infoMsg);
};
} // QmlDesigner

View File

@@ -0,0 +1,163 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "filepathmodel.h"
#include "exportnotification.h"
#include "projectexplorer/project.h"
#include "projectexplorer/projectnodes.h"
#include "utils/runextensions.h"
#include <QLoggingCategory>
#include <QTimer>
using namespace ProjectExplorer;
namespace {
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg)
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg)
void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
{
if (!project && !f.isCanceled())
f.reportFinished({});
int index = 0;
Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool {
if (f.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
f.reportResult(path, index++);
return true;
});
f.reportFinished();
}
}
namespace QmlDesigner {
FilePathModel::FilePathModel(ProjectExplorer::Project *project, QObject *parent)
: QAbstractListModel(parent),
m_project(project)
{
QTimer::singleShot(0, this, &FilePathModel::processProject);
}
FilePathModel::~FilePathModel()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
ExportNotification::addInfo(tr("Canceling QML files preparation."));
m_preprocessWatcher->cancel();
m_preprocessWatcher->waitForFinished();
qCDebug(loggerInfo) << "Canceling QML files preparation done.";
}
}
Qt::ItemFlags FilePathModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags itemFlags = QAbstractListModel::flags(index);
if (index.isValid())
itemFlags |= Qt::ItemIsUserCheckable;
return itemFlags;
}
int FilePathModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return m_files.count();
return 0;
}
QVariant FilePathModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
switch (role) {
case Qt::DisplayRole:
return m_files[index.row()].toUserOutput();
case Qt::CheckStateRole:
return m_skipped.count(m_files[index.row()]) ? Qt::Unchecked : Qt::Checked;
default:
break;
}
return {};
}
bool FilePathModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::CheckStateRole)
return false;
const Utils::FilePath path = m_files[index.row()];
if (value == Qt::Checked)
m_skipped.erase(path);
else
m_skipped.insert(path);
emit dataChanged(index, index);
return true;
}
Utils::FilePaths FilePathModel::files() const
{
Utils::FilePaths selectedPaths;
std::copy_if(m_files.begin(), m_files.end(), std::back_inserter(selectedPaths),
[this](const Utils::FilePath &path) {
return !m_skipped.count(path);
});
return selectedPaths;
}
void FilePathModel::processProject()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
qCDebug(loggerError) << "Previous model load not finished.";
return;
}
beginResetModel();
m_preprocessWatcher.reset(new QFutureWatcher<Utils::FilePath>(this));
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::resultReadyAt, this,
[this](int resultIndex) {
beginInsertRows(index(0, 0) , m_files.count(), m_files.count());
m_files.append(m_preprocessWatcher->resultAt(resultIndex));
endInsertRows();
});
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished,
this, &FilePathModel::endResetModel);
QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
}
}

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QAbstractListModel>
#include <QFutureWatcher>
#include "utils/fileutils.h"
#include <memory>
#include <unordered_set>
namespace ProjectExplorer {
class Project;
}
namespace QmlDesigner {
class FilePathModel : public QAbstractListModel
{
public:
FilePathModel(ProjectExplorer::Project *project, QObject *parent = nullptr);
~FilePathModel() override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Utils::FilePaths files() const;
private:
void processProject();
ProjectExplorer::Project *m_project = nullptr;
std::unique_ptr<QFutureWatcher<Utils::FilePath>> m_preprocessWatcher;
std::unordered_set<Utils::FilePath> m_skipped;
Utils::FilePaths m_files;
};
}

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "assetnodeparser.h"
#include "assetexportpluginconstants.h"
#include "assetexporter.h"
#include "qmlitemnode.h"
#include "componentexporter.h"
#include "utils/fileutils.h"
#include <QPixmap>
namespace QmlDesigner {
using namespace Constants;
AssetNodeParser::AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
ItemNodeParser(lineage, node)
{
}
bool AssetNodeParser::isExportable() const
{
auto hasType = [this](const QByteArray &type) {
return lineage().contains(type);
};
return hasType("QtQuick.Image") || hasType("QtQuick.Rectangle");
}
QJsonObject AssetNodeParser::json(Component &component) const
{
QJsonObject jsonObject = ItemNodeParser::json(component);
QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap();
Utils::FilePath assetPath = component.exporter().exportAsset(objectNode());
QJsonObject assetData;
assetData.insert(AssetPathTag, assetPath.toString());
jsonObject.insert(AssetDataTag, assetData);
return jsonObject;
}
}

View File

@@ -0,0 +1,42 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "modelitemnodeparser.h"
namespace QmlDesigner {
class Component;
class AssetNodeParser : public ItemNodeParser
{
public:
AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node);
~AssetNodeParser() override = default;
bool isExportable() const override;
int priority() const override { return 200; }
QJsonObject json(Component &component) const override;
};
}

View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "modelitemnodeparser.h"
#include "assetexportpluginconstants.h"
#include "qmlitemnode.h"
namespace QmlDesigner {
using namespace Constants;
ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage,
const ModelNode &node) :
ModelNodeParser(lineage, node)
{
}
bool QmlDesigner::ItemNodeParser::isExportable() const
{
return lineage().contains("QtQuick.Item");
}
QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component) const
{
Q_UNUSED(component);
const QmlObjectNode &qmlObjectNode = objectNode();
QJsonObject jsonObject;
jsonObject.insert(QmlIdTag, qmlObjectNode.id());
QmlItemNode itemNode = qmlObjectNode.toQmlItemNode();
// Position relative to parent
QPointF pos = itemNode.instancePosition();
jsonObject.insert(XPosTag, pos.x());
jsonObject.insert(YPosTag, pos.y());
// size
QSizeF size = itemNode.instanceSize();
jsonObject.insert(WidthTag, size.width());
jsonObject.insert(HeightTag, size.height());
return jsonObject;
}
}

View File

@@ -0,0 +1,44 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "modelnodeparser.h"
namespace QmlDesigner {
class ModelNode;
class Component;
class ItemNodeParser : public ModelNodeParser
{
public:
ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node);
~ItemNodeParser() override = default;
int priority() const override { return 100; }
bool isExportable() const override;
QJsonObject json(Component &component) const override;
};
}

View File

@@ -0,0 +1,41 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "modelnodeparser.h"
namespace QmlDesigner {
ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
m_node(node),
m_objectNode(node),
m_lineage(lineage)
{
}
QVariant ModelNodeParser::propertyValue(const PropertyName &name) const
{
return m_objectNode.instanceValue(name);
}
}

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "qmlobjectnode.h"
#include <QJsonObject>
#include <QByteArrayList>
namespace QmlDesigner {
class Component;
class ModelNode;
class ModelNodeParser
{
public:
ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node);
virtual ~ModelNodeParser() = default;
virtual int priority() const = 0;
virtual bool isExportable() const = 0;
virtual QJsonObject json(Component& component) const = 0;
const QByteArrayList& lineage() const { return m_lineage; }
const QmlObjectNode& objectNode() const { return m_objectNode; }
QVariant propertyValue(const PropertyName &name) const;
protected:
const ModelNode &m_node;
private:
QmlObjectNode m_objectNode;
QByteArrayList m_lineage;
};
}

View File

@@ -0,0 +1,87 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "textnodeparser.h"
#include "assetexportpluginconstants.h"
#include <QColor>
#include <QHash>
namespace {
const QHash<QString, QString> AlignMapping{
{"AlignRight", "RIGHT"},
{"AlignHCenter", "CENTER"},
{"AlignJustify", "JUSTIFIED"},
{"AlignLeft", "LEFT"},
{"AlignTop", "TOP"},
{"AlignVCenter", "CENTER"},
{"AlignBottom", "BOTTOM"}
};
QString toJsonAlignEnum(QString value) {
if (value.isEmpty() || !AlignMapping.contains(value))
return "";
return AlignMapping[value];
}
}
namespace QmlDesigner {
using namespace Constants;
TextNodeParser::TextNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
ItemNodeParser(lineage, node)
{
}
bool TextNodeParser::isExportable() const
{
return lineage().contains("QtQuick.Text");
}
QJsonObject TextNodeParser::json(Component &component) const
{
Q_UNUSED(component);
QJsonObject jsonObject = ItemNodeParser::json(component);
QJsonObject textDetails;
textDetails.insert(TextContentTag, propertyValue("text").toString());
textDetails.insert(FontFamilyTag, propertyValue("font.family").toString());
textDetails.insert(FontStyleTag, propertyValue("font.styleName").toString());
textDetails.insert(FontSizeTag, propertyValue("font.pixelSize").toInt());
textDetails.insert(LetterSpacingTag, propertyValue("font.letterSpacing").toFloat());
QColor fontColor(propertyValue("font.color").toString());
textDetails.insert(TextColorTag, fontColor.name(QColor::HexArgb));
textDetails.insert(HAlignTag, toJsonAlignEnum(propertyValue("horizontalAlignment").toString()));
textDetails.insert(VAlignTag, toJsonAlignEnum(propertyValue("verticalAlignment").toString()));
textDetails.insert(IsMultilineTag, propertyValue("wrapMode").toString().compare("NoWrap") != 0);
jsonObject.insert(TextDetailsTag, textDetails);
return jsonObject;
}
}

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "modelitemnodeparser.h"
namespace QmlDesigner {
class Component;
class TextNodeParser : public ItemNodeParser
{
public:
TextNodeParser(const QByteArrayList &lineage, const ModelNode &node);
~TextNodeParser() override = default;
bool isExportable() const override;
int priority() const override { return 200; }
QJsonObject json(Component &component) const override;
};
}

View File

@@ -83,6 +83,8 @@ const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedC
const char flowAssignEffectCommandId[] = "AssignFlowEffect"; const char flowAssignEffectCommandId[] = "AssignFlowEffect";
const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect"; const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect";
const char addToGroupItemCommandId[] = "AddToGroupItem"; const char addToGroupItemCommandId[] = "AddToGroupItem";
const char fitRootToScreenCommandId[] = "FitRootToScreen";
const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection");
const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect"); const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect");
@@ -155,6 +157,9 @@ const char layoutFillHeightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContext
const char flowAssignEffectDisplayName[] = "Assign FlowEffect "; const char flowAssignEffectDisplayName[] = "Assign FlowEffect ";
const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect "; const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect ";
const char fitRootToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit root to screen");
const char fitSelectionToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit selection to screen");
const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected item."); const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected item.");
const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected item."); const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected item.");
@@ -173,6 +178,9 @@ const char decreaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesi
const char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add item to stacked container."); const char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add item to stacked container.");
const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action."); const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action.");
const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space.");
const char fitSelectionToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the selected elements inside the available space.");
const int priorityFirst = 280; const int priorityFirst = 280;
const int prioritySelectionCategory = 220; const int prioritySelectionCategory = 220;
const int priorityQmlPreviewCategory = 200; const int priorityQmlPreviewCategory = 200;

View File

@@ -821,6 +821,27 @@ void DesignerActionManager::createDefaultDesignerActions()
&resetSize, &resetSize,
&selectionNotEmptyAndHasWidthOrHeightProperty)); &selectionNotEmptyAndHasWidthOrHeightProperty));
addDesignerAction(new ModelNodeAction(
fitRootToScreenCommandId,
fitRootToScreenDisplayName,
Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(),
fitRootToScreenToolTip,
genericToolBarCategory,
QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_0),
182,
&fitRootToScreen));
addDesignerAction(new ModelNodeAction(
fitSelectionToScreenCommandId,
fitSelectionToScreenDisplayName,
Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(),
fitSelectionToScreenToolTip,
genericToolBarCategory,
QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_I),
183,
&fitSelectionToScreen,
&selectionNotEmpty));
addDesignerAction(new SeperatorDesignerAction(editCategory, 170)); addDesignerAction(new SeperatorDesignerAction(editCategory, 170));
addDesignerAction(new VisiblityModelNodeAction( addDesignerAction(new VisiblityModelNodeAction(

View File

@@ -329,6 +329,23 @@ void resetPosition(const SelectionContext &selectionState)
}); });
} }
void fitRootToScreen(const SelectionContext &selectionState)
{
if (!selectionState.view())
return;
selectionState.view()->emitCustomNotification(QStringLiteral("fit root to screen"));
}
void fitSelectionToScreen(const SelectionContext &selectionState)
{
if (!selectionState.view())
return;
selectionState.view()->emitCustomNotification(QStringLiteral("fit selection to screen"),
selectionState.selectedModelNodes());
}
void goIntoComponentOperation(const SelectionContext &selectionState) void goIntoComponentOperation(const SelectionContext &selectionState)
{ {
goIntoComponent(selectionState.currentSingleSelectedNode()); goIntoComponent(selectionState.currentSingleSelectedNode());

View File

@@ -49,6 +49,8 @@ void setFillWidth(const SelectionContext &selectionState);
void setFillHeight(const SelectionContext &selectionState); void setFillHeight(const SelectionContext &selectionState);
void resetSize(const SelectionContext &selectionState); void resetSize(const SelectionContext &selectionState);
void resetPosition(const SelectionContext &selectionState); void resetPosition(const SelectionContext &selectionState);
void fitRootToScreen(const SelectionContext &selectionState);
void fitSelectionToScreen(const SelectionContext &selectionState);
void goIntoComponentOperation(const SelectionContext &selectionState); void goIntoComponentOperation(const SelectionContext &selectionState);
void setId(const SelectionContext &selectionState); void setId(const SelectionContext &selectionState);
void resetZ(const SelectionContext &selectionState); void resetZ(const SelectionContext &selectionState);

View File

@@ -30,7 +30,7 @@
namespace QmlDesigner { namespace QmlDesigner {
const int defaultZoomIndex = 11; const int defaultZoomIndex = 13;
ZoomAction::ZoomAction(QObject *parent) ZoomAction::ZoomAction(QObject *parent)
: QWidgetAction(parent), : QWidgetAction(parent),
@@ -69,14 +69,19 @@ void ZoomAction::setZoomLevel(float zoomLevel)
if (qFuzzyCompare(m_zoomLevel, zoomLevel)) if (qFuzzyCompare(m_zoomLevel, zoomLevel))
return; return;
forceZoomLevel(zoomLevel);
}
void ZoomAction::forceZoomLevel(float zoomLevel)
{
m_zoomLevel = qBound(0.01f, zoomLevel, 16.0f); m_zoomLevel = qBound(0.01f, zoomLevel, 16.0f);
emit zoomLevelChanged(m_zoomLevel); emit zoomLevelChanged(m_zoomLevel);
} }
//initial m_zoomLevel and m_currentComboBoxIndex //initial m_zoomLevel and m_currentComboBoxIndex
const QVector<float> s_zoomFactors = {0.01f, 0.02f, 0.05f, 0.0625f, 0.125f, 0.25f, const QVector<float> s_zoomFactors = {0.01f, 0.02f, 0.05f, 0.0625f, 0.1f, 0.125f, 0.2f, 0.25f,
0.33f, 0.5f, 0.66f, 0.75f, 0.9f, 1.0f, 1.25f, 0.33f, 0.5f, 0.66f, 0.75f, 0.9f, 1.0f, 1.1f, 1.25f, 1.33f,
1.5f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f }; 1.5f, 1.66f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f };
int getZoomIndex(float zoom) int getZoomIndex(float zoom)
{ {
@@ -87,6 +92,15 @@ int getZoomIndex(float zoom)
return -1; return -1;
} }
float ZoomAction::getClosestZoomLevel(float zoomLevel)
{
int i = 0;
while (i < s_zoomFactors.size() && s_zoomFactors[i] < zoomLevel)
++i;
return s_zoomFactors[qBound(0, i - 1, s_zoomFactors.size() - 1)];
}
QWidget *ZoomAction::createWidget(QWidget *parent) QWidget *ZoomAction::createWidget(QWidget *parent)
{ {
auto comboBox = new QComboBox(parent); auto comboBox = new QComboBox(parent);

View File

@@ -48,6 +48,9 @@ public:
void zoomOut(); void zoomOut();
void resetZoomLevel(); void resetZoomLevel();
void setZoomLevel(float zoomLevel); void setZoomLevel(float zoomLevel);
void forceZoomLevel(float zoomLevel);
static float getClosestZoomLevel(float zoomLevel);
protected: protected:
QWidget *createWidget(QWidget *parent) override; QWidget *createWidget(QWidget *parent) override;

View File

@@ -269,6 +269,9 @@ void ConnectionModel::addConnection()
if (QmlItemNode(selectedNode).isFlowActionArea()) if (QmlItemNode(selectedNode).isFlowActionArea())
source = selectedNode.validId() + ".trigger()"; source = selectedNode.validId() + ".trigger()";
if (QmlVisualNode(selectedNode).isFlowTransition())
source = selectedNode.validId() + ".trigger()";
if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty()) if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty())
newNode.bindingProperty("target").setExpression(selectedNode.id()); newNode.bindingProperty("target").setExpression(selectedNode.id());
else else

View File

@@ -112,7 +112,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
if (sceneState.contains(sceneKey)) { if (sceneState.contains(sceneKey)) {
qint32 newActiveScene = sceneState[sceneKey].value<qint32>(); qint32 newActiveScene = sceneState[sceneKey].value<qint32>();
edit3DWidget()->canvas()->updateActiveScene(newActiveScene); edit3DWidget()->canvas()->updateActiveScene(newActiveScene);
rootModelNode().setAuxiliaryData("3d-active-scene", newActiveScene); rootModelNode().setAuxiliaryData("active3dScene", newActiveScene);
} }
if (sceneState.contains(selectKey)) if (sceneState.contains(selectKey))

View File

@@ -129,7 +129,7 @@ void FormEditorItem::setup()
QRectF FormEditorItem::boundingRect() const QRectF FormEditorItem::boundingRect() const
{ {
return m_boundingRect.adjusted(-2, -2, 2, 2); return m_boundingRect;
} }
QPainterPath FormEditorItem::shape() const QPainterPath FormEditorItem::shape() const
@@ -150,7 +150,7 @@ void FormEditorItem::updateGeometry()
prepareGeometryChange(); prepareGeometryChange();
m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect(); m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect();
m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect); m_boundingRect = qmlItemNode().instanceBoundingRect();
setTransform(qmlItemNode().instanceTransformWithContentTransform()); setTransform(qmlItemNode().instanceTransformWithContentTransform());
// the property for zValue is called z in QGraphicsObject // the property for zValue is called z in QGraphicsObject
if (qmlItemNode().instanceValue("z").isValid() && !qmlItemNode().isRootModelNode()) if (qmlItemNode().instanceValue("z").isValid() && !qmlItemNode().isRootModelNode())
@@ -739,7 +739,7 @@ static bool isValid(const QList<QmlItemNode> &list)
if (!item.isValid()) if (!item.isValid())
return false; return false;
return true; return !list.isEmpty();
} }
static bool isModelNodeValid(const QList<QmlItemNode> &list) static bool isModelNodeValid(const QList<QmlItemNode> &list)
@@ -748,7 +748,7 @@ static bool isModelNodeValid(const QList<QmlItemNode> &list)
if (!item.modelNode().isValid()) if (!item.modelNode().isValid())
return false; return false;
return true; return !list.isEmpty();
} }
class ResolveConnection class ResolveConnection
@@ -797,17 +797,17 @@ public:
if (f.isValid()) { if (f.isValid()) {
for (const QmlFlowActionAreaNode &area : f.flowActionAreas()) { for (const QmlFlowActionAreaNode &area : f.flowActionAreas()) {
ModelNode target = area.targetTransition(); ModelNode target = area.targetTransition();
if (target == node.modelNode()) { if (target == node.modelNode())
areaNode = area; areaNode = area;
} else {
const ModelNode decisionNode = area.decisionNodeForTransition(node.modelNode());
if (decisionNode.isValid()) {
from.clear();
from.append(decisionNode);
areaNode = ModelNode();
}
}
} }
const ModelNode decisionNode = QmlFlowItemNode::decisionNodeForTransition(node.modelNode());
if (decisionNode.isValid()) {
from.clear();
from.append(decisionNode);
areaNode = ModelNode();
}
if (f.modelNode().hasAuxiliaryData("joinConnection")) if (f.modelNode().hasAuxiliaryData("joinConnection"))
joinConnection = f.modelNode().auxiliaryData("joinConnection").toBool(); joinConnection = f.modelNode().auxiliaryData("joinConnection").toBool();
} else { } else {

View File

@@ -442,12 +442,49 @@ void FormEditorView::documentMessagesChanged(const QList<DocumentMessage> &error
m_formEditorWidget->hideErrorMessageBox(); m_formEditorWidget->hideErrorMessageBox();
} }
void FormEditorView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList<ModelNode> &/*nodeList*/, const QList<QVariant> &/*data*/) void FormEditorView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &/*data*/)
{ {
if (identifier == QLatin1String("puppet crashed")) if (identifier == QLatin1String("puppet crashed"))
m_dragTool->clearMoveDelay(); m_dragTool->clearMoveDelay();
if (identifier == QLatin1String("reset QmlPuppet")) if (identifier == QLatin1String("reset QmlPuppet"))
temporaryBlockView(); temporaryBlockView();
if (identifier == QLatin1String("fit root to screen")) {
if (QmlItemNode(rootModelNode()).isFlowView()) {
QRectF boundingRect;
for (QGraphicsItem *item : scene()->items()) {
if (auto formEditorItem = FormEditorItem::fromQGraphicsItem(item)) {
if (!formEditorItem->qmlItemNode().modelNode().isRootNode()
&& !formEditorItem->sceneBoundingRect().isNull())
boundingRect = boundingRect.united(formEditorItem->sceneBoundingRect());
}
}
m_formEditorWidget->graphicsView()->fitInView(boundingRect,
Qt::KeepAspectRatio);
} else {
m_formEditorWidget->graphicsView()->fitInView(m_formEditorWidget->rootItemRect(),
Qt::KeepAspectRatio);
}
const qreal scaleFactor = m_formEditorWidget->graphicsView()->viewportTransform().m11();
float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor);
m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel);
}
if (identifier == QLatin1String("fit selection to screen")) {
if (nodeList.isEmpty())
return;
QRectF boundingRect;
for (const ModelNode &node : nodeList) {
if (FormEditorItem *item = scene()->itemForQmlItemNode(node))
boundingRect = boundingRect.united(item->sceneBoundingRect());
}
m_formEditorWidget->graphicsView()->fitInView(boundingRect,
Qt::KeepAspectRatio);
const qreal scaleFactor = m_formEditorWidget->graphicsView()->viewportTransform().m11();
float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor);
m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel);
}
} }
AbstractFormEditorTool *FormEditorView::currentTool() const AbstractFormEditorTool *FormEditorView::currentTool() const

View File

@@ -469,7 +469,7 @@ void DesignDocument::paste()
[](const ModelNode &node) { return !node.isSubclassOf("QtQuick3D.Node"); }) [](const ModelNode &node) { return !node.isSubclassOf("QtQuick3D.Node"); })
== selectedNodes.end(); == selectedNodes.end();
if (all3DNodes) { if (all3DNodes) {
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt(); int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt();
if (activeSceneId != -1) { if (activeSceneId != -1) {
NodeListProperty sceneNodeProperty NodeListProperty sceneNodeProperty
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId); = QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);
@@ -515,7 +515,7 @@ void DesignDocument::paste()
} else { } else {
// if selection is empty and this is a 3D Node, paste it under the active scene // if selection is empty and this is a 3D Node, paste it under the active scene
if (pastedNode.isSubclassOf("QtQuick3D.Node")) { if (pastedNode.isSubclassOf("QtQuick3D.Node")) {
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt(); int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt();
if (activeSceneId != -1) { if (activeSceneId != -1) {
NodeListProperty sceneNodeProperty NodeListProperty sceneNodeProperty
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId); = QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);

View File

@@ -469,8 +469,8 @@ void PropertyEditorView::setupQmlBackend()
if (m_selectedNode.isValid()) { if (m_selectedNode.isValid()) {
qmlObjectNode = QmlObjectNode(m_selectedNode); qmlObjectNode = QmlObjectNode(m_selectedNode);
Q_ASSERT(qmlObjectNode.isValid()); Q_ASSERT(qmlObjectNode.isValid());
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
} }
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false)); currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false));
if (specificQmlData.isEmpty()) if (specificQmlData.isEmpty())
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);

View File

@@ -31,12 +31,12 @@
namespace QmlDesigner { namespace QmlDesigner {
TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene) TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene)
: m_scene(scene) : m_scene(scene)
, m_delegate(nullptr) , m_delegate(nullptr)
{} {}
TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene, TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene,
TimelineToolDelegate *delegate) TimelineToolDelegate *delegate)
: m_scene(scene) : m_scene(scene)
, m_delegate(delegate) , m_delegate(delegate)
@@ -44,7 +44,7 @@ TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene,
TimelineAbstractTool::~TimelineAbstractTool() = default; TimelineAbstractTool::~TimelineAbstractTool() = default;
TimelineGraphicsScene *TimelineAbstractTool::scene() const AbstractScrollGraphicsScene *TimelineAbstractTool::scene() const
{ {
return m_scene; return m_scene;
} }

View File

@@ -36,14 +36,14 @@ namespace QmlDesigner {
enum class ToolType { Move, Select }; enum class ToolType { Move, Select };
class TimelineMovableAbstractItem; class TimelineMovableAbstractItem;
class TimelineGraphicsScene; class AbstractScrollGraphicsScene;
class TimelineToolDelegate; class TimelineToolDelegate;
class TimelineAbstractTool class TimelineAbstractTool
{ {
public: public:
explicit TimelineAbstractTool(TimelineGraphicsScene *scene); explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene);
explicit TimelineAbstractTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
virtual ~TimelineAbstractTool(); virtual ~TimelineAbstractTool();
virtual void mousePressEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) = 0; virtual void mousePressEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) = 0;
@@ -56,7 +56,7 @@ public:
virtual void keyPressEvent(QKeyEvent *keyEvent) = 0; virtual void keyPressEvent(QKeyEvent *keyEvent) = 0;
virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0; virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0;
TimelineGraphicsScene *scene() const; AbstractScrollGraphicsScene *scene() const;
TimelineToolDelegate *delegate() const; TimelineToolDelegate *delegate() const;
@@ -65,7 +65,7 @@ public:
TimelineMovableAbstractItem *currentItem() const; TimelineMovableAbstractItem *currentItem() const;
private: private:
TimelineGraphicsScene *m_scene; AbstractScrollGraphicsScene *m_scene;
TimelineToolDelegate *m_delegate; TimelineToolDelegate *m_delegate;
}; };

View File

@@ -91,7 +91,7 @@ QList<QmlTimelineKeyframeGroup> allTimelineFrames(const QmlTimeline &timeline)
} }
TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *parent) TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *parent)
: QGraphicsScene(parent) : AbstractScrollGraphicsScene(parent)
, m_parent(parent) , m_parent(parent)
, m_layout(new TimelineGraphicsLayout(this)) , m_layout(new TimelineGraphicsLayout(this))
, m_currentFrameIndicator(new TimelineFrameHandle) , m_currentFrameIndicator(new TimelineFrameHandle)
@@ -378,17 +378,17 @@ void TimelineGraphicsScene::commitCurrentFrame(qreal frame)
} }
} }
QList<TimelineKeyframeItem *> TimelineGraphicsScene::selectedKeyframes() const QList<TimelineKeyframeItem *> AbstractScrollGraphicsScene::selectedKeyframes() const
{ {
return m_selectedKeyframes; return m_selectedKeyframes;
} }
bool TimelineGraphicsScene::hasSelection() const bool AbstractScrollGraphicsScene::hasSelection() const
{ {
return !m_selectedKeyframes.empty(); return !m_selectedKeyframes.empty();
} }
bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const bool AbstractScrollGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const
{ {
if (m_selectedKeyframes.empty()) if (m_selectedKeyframes.empty())
return false; return false;
@@ -396,12 +396,12 @@ bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const
return m_selectedKeyframes.back() == keyframe; return m_selectedKeyframes.back() == keyframe;
} }
bool TimelineGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const bool AbstractScrollGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const
{ {
return m_selectedKeyframes.contains(keyframe); return m_selectedKeyframes.contains(keyframe);
} }
bool TimelineGraphicsScene::multipleKeyframesSelected() const bool AbstractScrollGraphicsScene::multipleKeyframesSelected() const
{ {
return m_selectedKeyframes.count() > 1; return m_selectedKeyframes.count() > 1;
} }
@@ -456,19 +456,19 @@ void TimelineGraphicsScene::invalidateRecordButtonsStatus()
TimelinePropertyItem::updateRecordButtonStatus(item); TimelinePropertyItem::updateRecordButtonStatus(item);
} }
int TimelineGraphicsScene::scrollOffset() const int AbstractScrollGraphicsScene::scrollOffset() const
{ {
return m_scrollOffset; return m_scrollOffset;
} }
void TimelineGraphicsScene::setScrollOffset(int offset) void AbstractScrollGraphicsScene::setScrollOffset(int offset)
{ {
m_scrollOffset = offset; m_scrollOffset = offset;
emitScrollOffsetChanged(); emitScrollOffsetChanged();
update(); update();
} }
QGraphicsView *TimelineGraphicsScene::graphicsView() const QGraphicsView *AbstractScrollGraphicsScene::graphicsView() const
{ {
for (auto *v : views()) for (auto *v : views())
if (v->objectName() == "SceneView") if (v->objectName() == "SceneView")
@@ -477,7 +477,7 @@ QGraphicsView *TimelineGraphicsScene::graphicsView() const
return nullptr; return nullptr;
} }
QGraphicsView *TimelineGraphicsScene::rulerView() const QGraphicsView *AbstractScrollGraphicsScene::rulerView() const
{ {
for (auto *v : views()) for (auto *v : views())
if (v->objectName() == "RulerView") if (v->objectName() == "RulerView")
@@ -491,7 +491,7 @@ QmlTimeline TimelineGraphicsScene::currentTimeline() const
return QmlTimeline(timelineModelNode()); return QmlTimeline(timelineModelNode());
} }
QRectF TimelineGraphicsScene::selectionBounds() const QRectF AbstractScrollGraphicsScene::selectionBounds() const
{ {
QRectF bbox; QRectF bbox;
@@ -501,7 +501,7 @@ QRectF TimelineGraphicsScene::selectionBounds() const
return bbox; return bbox;
} }
void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode, void AbstractScrollGraphicsScene::selectKeyframes(const SelectionMode &mode,
const QList<TimelineKeyframeItem *> &items) const QList<TimelineKeyframeItem *> &items)
{ {
if (mode == SelectionMode::Remove || mode == SelectionMode::Toggle) { if (mode == SelectionMode::Remove || mode == SelectionMode::Toggle) {
@@ -536,13 +536,14 @@ void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode,
emit selectionChanged(); emit selectionChanged();
} }
void TimelineGraphicsScene::clearSelection() void AbstractScrollGraphicsScene::clearSelection()
{ {
for (auto *keyframe : m_selectedKeyframes) for (auto *keyframe : m_selectedKeyframes)
if (keyframe) if (keyframe)
keyframe->setHighlighted(false); keyframe->setHighlighted(false);
m_selectedKeyframes.clear(); m_selectedKeyframes.clear();
emit selectionChanged();
} }
QList<QGraphicsItem *> TimelineGraphicsScene::itemsAt(const QPointF &pos) QList<QGraphicsItem *> TimelineGraphicsScene::itemsAt(const QPointF &pos)
@@ -682,7 +683,7 @@ ModelNode TimelineGraphicsScene::timelineModelNode() const
void TimelineGraphicsScene::handleKeyframeDeletion() void TimelineGraphicsScene::handleKeyframeDeletion()
{ {
QList<ModelNode> nodesToBeDeleted; QList<ModelNode> nodesToBeDeleted;
for (auto keyframe : m_selectedKeyframes) { for (auto keyframe : selectedKeyframes()) {
nodesToBeDeleted.append(keyframe->frameNode()); nodesToBeDeleted.append(keyframe->frameNode());
} }
deleteKeyframes(nodesToBeDeleted); deleteKeyframes(nodesToBeDeleted);
@@ -711,7 +712,7 @@ void TimelineGraphicsScene::pasteKeyframesToTarget(const ModelNode &targetNode)
void TimelineGraphicsScene::copySelectedKeyframes() void TimelineGraphicsScene::copySelectedKeyframes()
{ {
TimelineActions::copyKeyframes( TimelineActions::copyKeyframes(
Utils::transform(m_selectedKeyframes, &TimelineKeyframeItem::frameNode)); Utils::transform(selectedKeyframes(), &TimelineKeyframeItem::frameNode));
} }
void TimelineGraphicsScene::pasteSelectedKeyframes() void TimelineGraphicsScene::pasteSelectedKeyframes()
@@ -756,7 +757,20 @@ void TimelineGraphicsScene::activateLayout()
m_layout->activate(); m_layout->activate();
} }
void TimelineGraphicsScene::emitScrollOffsetChanged() AbstractView *TimelineGraphicsScene::abstractView() const
{
return timelineView();
}
int AbstractScrollGraphicsScene::getScrollOffset(QGraphicsScene *scene)
{
auto scrollScene = qobject_cast<AbstractScrollGraphicsScene*>(scene);
if (scrollScene)
return scrollScene->scrollOffset();
return 0;
}
void AbstractScrollGraphicsScene::emitScrollOffsetChanged()
{ {
for (QGraphicsItem *item : items()) for (QGraphicsItem *item : items())
TimelineMovableAbstractItem::emitScrollOffsetChanged(item); TimelineMovableAbstractItem::emitScrollOffsetChanged(item);
@@ -783,4 +797,8 @@ bool TimelineGraphicsScene::event(QEvent *event)
} }
} }
QmlDesigner::AbstractScrollGraphicsScene::AbstractScrollGraphicsScene(QWidget *parent)
: QGraphicsScene(parent)
{}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -36,6 +36,7 @@
QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout) QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout)
QT_FORWARD_DECLARE_CLASS(QComboBox) QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QWidget)
namespace QmlDesigner { namespace QmlDesigner {
@@ -51,15 +52,66 @@ class TimelinePlaceholder;
class TimelineGraphicsLayout; class TimelineGraphicsLayout;
class TimelineToolBar; class TimelineToolBar;
class TimelineGraphicsScene : public QGraphicsScene class AbstractScrollGraphicsScene : public QGraphicsScene
{ {
Q_OBJECT Q_OBJECT
signals: public:
void selectionChanged(); AbstractScrollGraphicsScene(QWidget *parent);
;
int scrollOffset() const;
void setScrollOffset(int offset);
static int getScrollOffset(QGraphicsScene *scene);
QRectF selectionBounds() const;
void selectKeyframes(const SelectionMode &mode, const QList<TimelineKeyframeItem *> &items);
virtual void clearSelection();
QList<TimelineKeyframeItem *> selectedKeyframes() const;
bool hasSelection() const;
bool isCurrent(TimelineKeyframeItem *keyframe) const;
bool isKeyframeSelected(TimelineKeyframeItem *keyframe) const;
bool multipleKeyframesSelected() const;
virtual qreal rulerScaling() const = 0;
virtual int rulerWidth() const = 0;
virtual qreal rulerDuration() const = 0;
virtual AbstractView *abstractView() const = 0;
virtual void setCurrentFrame(int) {}
virtual qreal startFrame() const = 0;
virtual qreal endFrame() const = 0;
virtual void invalidateScrollbar() = 0;
virtual qreal snap(qreal frame, bool snapToPlayhead = true)
{
Q_UNUSED(snapToPlayhead);
return frame;
}
QGraphicsView *graphicsView() const;
QGraphicsView *rulerView() const;
signals:
void statusBarMessageChanged(const QString &message);
void selectionChanged();
void scroll(const TimelineUtils::Side &side); void scroll(const TimelineUtils::Side &side);
private:
void emitScrollOffsetChanged();
int m_scrollOffset = 0;
QList<TimelineKeyframeItem *> m_selectedKeyframes;
};
class TimelineGraphicsScene : public AbstractScrollGraphicsScene
{
Q_OBJECT
public: public:
explicit TimelineGraphicsScene(TimelineWidget *parent); explicit TimelineGraphicsScene(TimelineWidget *parent);
@@ -74,7 +126,7 @@ public:
void invalidateLayout(); void invalidateLayout();
qreal setCurrenFrame(const QmlTimeline &timeline, qreal frame); qreal setCurrenFrame(const QmlTimeline &timeline, qreal frame);
void setCurrentFrame(int frame); void setCurrentFrame(int frame) override;
void setStartFrame(int frame); void setStartFrame(int frame);
void setEndFrame(int frame); void setEndFrame(int frame);
@@ -82,11 +134,12 @@ public:
TimelineWidget *timelineWidget() const; TimelineWidget *timelineWidget() const;
TimelineToolBar *toolBar() const; TimelineToolBar *toolBar() const;
qreal rulerScaling() const; qreal rulerScaling() const override;
int rulerWidth() const; int rulerWidth() const override;
qreal rulerDuration() const; qreal rulerDuration() const override;
qreal startFrame() const;
qreal endFrame() const; qreal startFrame() const override;
qreal endFrame() const override;
void updateKeyframePositionsCache(); void updateKeyframePositionsCache();
@@ -97,39 +150,22 @@ public:
QVector<qreal> keyframePositions() const; QVector<qreal> keyframePositions() const;
QVector<qreal> keyframePositions(const QmlTimelineKeyframeGroup &frames) const; QVector<qreal> keyframePositions(const QmlTimelineKeyframeGroup &frames) const;
qreal snap(qreal frame, bool snapToPlayhead = true); qreal snap(qreal frame, bool snapToPlayhead = true) override;
void setRulerScaling(int scaling); void setRulerScaling(int scaling);
void commitCurrentFrame(qreal frame); void commitCurrentFrame(qreal frame);
QList<TimelineKeyframeItem *> selectedKeyframes() const;
bool hasSelection() const;
bool isCurrent(TimelineKeyframeItem *keyframe) const;
bool isKeyframeSelected(TimelineKeyframeItem *keyframe) const;
bool multipleKeyframesSelected() const;
void invalidateSectionForTarget(const ModelNode &modelNode); void invalidateSectionForTarget(const ModelNode &modelNode);
void invalidateKeyframesForTarget(const ModelNode &modelNode); void invalidateKeyframesForTarget(const ModelNode &modelNode);
void invalidateScene(); void invalidateScene();
void invalidateScrollbar(); void invalidateScrollbar() override;
void invalidateCurrentValues(); void invalidateCurrentValues();
void invalidateRecordButtonsStatus(); void invalidateRecordButtonsStatus();
int scrollOffset() const;
void setScrollOffset(int offset);
QGraphicsView *graphicsView() const;
QGraphicsView *rulerView() const;
QmlTimeline currentTimeline() const; QmlTimeline currentTimeline() const;
QRectF selectionBounds() const;
void selectKeyframes(const SelectionMode &mode, const QList<TimelineKeyframeItem *> &items);
void clearSelection();
void handleKeyframeDeletion(); void handleKeyframeDeletion();
void deleteAllKeyframesForTarget(const ModelNode &targetNode); void deleteAllKeyframesForTarget(const ModelNode &targetNode);
void insertAllKeyframesForTarget(const ModelNode &targetNode); void insertAllKeyframesForTarget(const ModelNode &targetNode);
@@ -143,8 +179,7 @@ public:
void activateLayout(); void activateLayout();
signals: AbstractView *abstractView() const override;
void statusBarMessageChanged(const QString &message);
protected: protected:
bool event(QEvent *event) override; bool event(QEvent *event) override;
@@ -163,7 +198,6 @@ private:
void invalidateSections(); void invalidateSections();
ModelNode timelineModelNode() const; ModelNode timelineModelNode() const;
void emitScrollOffsetChanged();
void emitStatusBarPlayheadFrameChanged(int frame); void emitStatusBarPlayheadFrameChanged(int frame);
QList<QGraphicsItem *> itemsAt(const QPointF &pos); QList<QGraphicsItem *> itemsAt(const QPointF &pos);
@@ -178,12 +212,8 @@ private:
TimelineToolDelegate m_tools; TimelineToolDelegate m_tools;
QList<TimelineKeyframeItem *> m_selectedKeyframes;
// sorted, unique cache of keyframes positions, used for snapping // sorted, unique cache of keyframes positions, used for snapping
QVector<qreal> m_keyframePositionsCache; QVector<qreal> m_keyframePositionsCache;
int m_scrollOffset = 0;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -52,8 +52,7 @@ TimelineItem::TimelineItem(TimelineItem *parent)
TimelineGraphicsScene *TimelineItem::timelineScene() const TimelineGraphicsScene *TimelineItem::timelineScene() const
{ {
return static_cast<TimelineGraphicsScene *>(scene()); return qobject_cast<TimelineGraphicsScene *>(scene());
;
} }
TimelineFrameHandle::TimelineFrameHandle(TimelineItem *parent) TimelineFrameHandle::TimelineFrameHandle(TimelineItem *parent)
@@ -93,7 +92,7 @@ void TimelineFrameHandle::setPosition(qreal frame)
void TimelineFrameHandle::setPositionInteractive(const QPointF &position) void TimelineFrameHandle::setPositionInteractive(const QPointF &position)
{ {
const double width = timelineScene()->width(); const double width = abstractScrollGraphicsScene()->width();
if (position.x() > width) { if (position.x() > width) {
callSetClampedXPosition(width - (rect().width() / 2) - 1); callSetClampedXPosition(width - (rect().width() / 2) - 1);
@@ -104,7 +103,7 @@ void TimelineFrameHandle::setPositionInteractive(const QPointF &position)
} else { } else {
callSetClampedXPosition(position.x() - rect().width() / 2); callSetClampedXPosition(position.x() - rect().width() / 2);
const qreal frame = std::round(mapFromSceneToFrame(rect().center().x())); const qreal frame = std::round(mapFromSceneToFrame(rect().center().x()));
timelineScene()->commitCurrentFrame(frame); timelineGraphicsScene()->commitCurrentFrame(frame);
} }
} }
@@ -128,6 +127,11 @@ TimelineFrameHandle *TimelineFrameHandle::asTimelineFrameHandle()
return this; return this;
} }
TimelineGraphicsScene *TimelineFrameHandle::timelineGraphicsScene() const
{
return qobject_cast<TimelineGraphicsScene* >(abstractScrollGraphicsScene());
}
void TimelineFrameHandle::scrollOffsetChanged() void TimelineFrameHandle::scrollOffsetChanged()
{ {
setPosition(position()); setPosition(position());
@@ -182,7 +186,7 @@ void TimelineFrameHandle::paint(QPainter *painter,
QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const
{ {
for (auto *view : timelineScene()->views()) { for (auto *view : abstractScrollGraphicsScene()->views()) {
if (view->objectName() == "SceneView") { if (view->objectName() == "SceneView") {
auto graphicsViewCoords = view->mapFromGlobal(pos); auto graphicsViewCoords = view->mapFromGlobal(pos);
auto sceneCoords = view->mapToScene(graphicsViewCoords); auto sceneCoords = view->mapToScene(graphicsViewCoords);
@@ -195,7 +199,7 @@ QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const
int TimelineFrameHandle::computeScrollSpeed() const int TimelineFrameHandle::computeScrollSpeed() const
{ {
const double mouse = mapFromGlobal(QCursor::pos()).x(); const double mouse = mapFromGlobal(QCursor::pos()).x();
const double width = timelineScene()->width(); const double width = abstractScrollGraphicsScene()->width();
const double acc = mouse > width ? mouse - width const double acc = mouse > width ? mouse - width
: double(TimelineConstants::sectionWidth) - mouse; : double(TimelineConstants::sectionWidth) - mouse;
@@ -216,7 +220,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x)
const int minimumWidth = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset const int minimumWidth = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
- rect().width() / 2; - rect().width() / 2;
const int maximumWidth = minimumWidth const int maximumWidth = minimumWidth
+ timelineScene()->rulerDuration() * timelineScene()->rulerScaling() + abstractScrollGraphicsScene()->rulerDuration() * abstractScrollGraphicsScene()->rulerScaling()
- scrollOffset(); - scrollOffset();
setClampedXPosition(x, minimumWidth, maximumWidth); setClampedXPosition(x, minimumWidth, maximumWidth);
@@ -225,7 +229,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x)
// Auto scroll when dragging playhead out of bounds. // Auto scroll when dragging playhead out of bounds.
void TimelineFrameHandle::scrollOutOfBounds() void TimelineFrameHandle::scrollOutOfBounds()
{ {
const double width = timelineScene()->width(); const double width = abstractScrollGraphicsScene()->width();
const double mouse = mapFromGlobal(QCursor::pos()).x(); const double mouse = mapFromGlobal(QCursor::pos()).x();
if (mouse > width) if (mouse > width)
@@ -236,14 +240,14 @@ void TimelineFrameHandle::scrollOutOfBounds()
void TimelineFrameHandle::scrollOutOfBoundsMax() void TimelineFrameHandle::scrollOutOfBoundsMax()
{ {
const double width = timelineScene()->width(); const double width = abstractScrollGraphicsScene()->width();
if (QApplication::mouseButtons() == Qt::LeftButton) { if (QApplication::mouseButtons() == Qt::LeftButton) {
const double frameWidth = timelineScene()->rulerScaling(); const double frameWidth = abstractScrollGraphicsScene()->rulerScaling();
const double upperThreshold = width - frameWidth; const double upperThreshold = width - frameWidth;
if (rect().center().x() > upperThreshold) { if (rect().center().x() > upperThreshold) {
timelineScene()->setScrollOffset(computeScrollSpeed()); abstractScrollGraphicsScene()->setScrollOffset(computeScrollSpeed());
timelineScene()->invalidateScrollbar(); abstractScrollGraphicsScene()->invalidateScrollbar();
} }
callSetClampedXPosition(width - (rect().width() / 2) - 1); callSetClampedXPosition(width - (rect().width() / 2) - 1);
@@ -253,8 +257,8 @@ void TimelineFrameHandle::scrollOutOfBoundsMax()
callSetClampedXPosition(width - (rect().width() / 2) - 1); callSetClampedXPosition(width - (rect().width() / 2) - 1);
const int frame = std::floor(mapFromSceneToFrame(rect().center().x())); const int frame = std::floor(mapFromSceneToFrame(rect().center().x()));
const int ef = timelineScene()->endFrame(); const int ef = abstractScrollGraphicsScene()->endFrame();
timelineScene()->commitCurrentFrame(frame <= ef ? frame : ef); timelineGraphicsScene()->commitCurrentFrame(frame <= ef ? frame : ef);
} }
} }
@@ -264,11 +268,11 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
auto offset = computeScrollSpeed(); auto offset = computeScrollSpeed();
if (offset >= 0) if (offset >= 0)
timelineScene()->setScrollOffset(offset); abstractScrollGraphicsScene()->setScrollOffset(offset);
else else
timelineScene()->setScrollOffset(0); abstractScrollGraphicsScene()->setScrollOffset(0);
timelineScene()->invalidateScrollbar(); abstractScrollGraphicsScene()->invalidateScrollbar();
callSetClampedXPosition(TimelineConstants::sectionWidth); callSetClampedXPosition(TimelineConstants::sectionWidth);
m_timer.start(); m_timer.start();
@@ -278,7 +282,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
int frame = mapFromSceneToFrame(rect().center().x()); int frame = mapFromSceneToFrame(rect().center().x());
const int sframe = timelineScene()->startFrame(); const int sframe = abstractScrollGraphicsScene()->startFrame();
if (frame != sframe) { if (frame != sframe) {
const qreal framePos = mapFromFrameToScene(frame); const qreal framePos = mapFromFrameToScene(frame);
@@ -287,7 +291,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
frame++; frame++;
} }
timelineScene()->commitCurrentFrame(frame >= sframe ? frame : sframe); timelineGraphicsScene()->commitCurrentFrame(frame >= sframe ? frame : sframe);
} }
} }

View File

@@ -35,6 +35,8 @@ QT_FORWARD_DECLARE_CLASS(QPainterPath)
namespace QmlDesigner { namespace QmlDesigner {
class TimelineGraphicsScene;
class TimelineItem : public QGraphicsWidget class TimelineItem : public QGraphicsWidget
{ {
Q_OBJECT Q_OBJECT
@@ -59,6 +61,8 @@ public:
TimelineFrameHandle *asTimelineFrameHandle() override; TimelineFrameHandle *asTimelineFrameHandle() override;
TimelineGraphicsScene *timelineGraphicsScene() const;
protected: protected:
void scrollOffsetChanged() override; void scrollOffsetChanged() override;
QPainterPath shape() const override; QPainterPath shape() const override;

View File

@@ -53,7 +53,7 @@ void TimelineMovableAbstractItem::itemDoubleClicked()
int TimelineMovableAbstractItem::scrollOffset() const int TimelineMovableAbstractItem::scrollOffset() const
{ {
return timelineScene()->scrollOffset(); return abstractScrollGraphicsScene()->scrollOffset();
} }
int TimelineMovableAbstractItem::xPosScrollOffset(int x) const int TimelineMovableAbstractItem::xPosScrollOffset(int x) const
@@ -63,7 +63,7 @@ int TimelineMovableAbstractItem::xPosScrollOffset(int x) const
qreal TimelineMovableAbstractItem::mapFromFrameToScene(qreal x) const qreal TimelineMovableAbstractItem::mapFromFrameToScene(qreal x) const
{ {
return TimelineConstants::sectionWidth + (x - timelineScene()->startFrame()) * rulerScaling() return TimelineConstants::sectionWidth + (x - abstractScrollGraphicsScene()->startFrame()) * rulerScaling()
- scrollOffset() + TimelineConstants::timelineLeftOffset; - scrollOffset() + TimelineConstants::timelineLeftOffset;
} }
@@ -71,8 +71,8 @@ qreal TimelineMovableAbstractItem::mapFromSceneToFrame(qreal x) const
{ {
return xPosScrollOffset(x - TimelineConstants::sectionWidth return xPosScrollOffset(x - TimelineConstants::sectionWidth
- TimelineConstants::timelineLeftOffset) - TimelineConstants::timelineLeftOffset)
/ timelineScene()->rulerScaling() / abstractScrollGraphicsScene()->rulerScaling()
+ timelineScene()->startFrame(); + abstractScrollGraphicsScene()->startFrame();
} }
void TimelineMovableAbstractItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void TimelineMovableAbstractItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
@@ -133,7 +133,7 @@ TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem(QGraph
qreal TimelineMovableAbstractItem::rulerScaling() const qreal TimelineMovableAbstractItem::rulerScaling() const
{ {
return static_cast<TimelineGraphicsScene *>(scene())->rulerScaling(); return qobject_cast<AbstractScrollGraphicsScene *>(scene())->rulerScaling();
} }
int TimelineMovableAbstractItem::type() const int TimelineMovableAbstractItem::type() const
@@ -141,9 +141,9 @@ int TimelineMovableAbstractItem::type() const
return Type; return Type;
} }
TimelineGraphicsScene *TimelineMovableAbstractItem::timelineScene() const AbstractScrollGraphicsScene *TimelineMovableAbstractItem::abstractScrollGraphicsScene() const
{ {
return static_cast<TimelineGraphicsScene *>(scene()); return qobject_cast<AbstractScrollGraphicsScene *>(scene());
} }
TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem() TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem()

View File

@@ -32,7 +32,7 @@
namespace QmlDesigner { namespace QmlDesigner {
class TimelineGraphicsScene; class AbstractScrollGraphicsScene;
class TimelineKeyframeItem; class TimelineKeyframeItem;
class TimelineFrameHandle; class TimelineFrameHandle;
@@ -75,7 +75,7 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void setClampedXPosition(qreal x, qreal min, qreal max); void setClampedXPosition(qreal x, qreal min, qreal max);
TimelineGraphicsScene *timelineScene() const; AbstractScrollGraphicsScene *abstractScrollGraphicsScene() const;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -61,7 +61,7 @@ QPointF mapToItem(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *e
return event->scenePos(); return event->scenePos();
} }
TimelineMoveTool::TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate) TimelineMoveTool::TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate)
: TimelineAbstractTool(scene, delegate) : TimelineAbstractTool(scene, delegate)
{} {}
@@ -155,7 +155,7 @@ void TimelineMoveTool::mouseReleaseEvent(TimelineMovableAbstractItem *item,
} }
} }
scene()->timelineView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent", scene()->abstractView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent",
[this, current]() { [this, current]() {
current->commitPosition(mapToItem(current, current->rect().center())); current->commitPosition(mapToItem(current, current->rect().center()));

View File

@@ -41,7 +41,7 @@ class TimelineMoveTool : public TimelineAbstractTool
Q_DECLARE_TR_FUNCTIONS(TimelineMoveTool) Q_DECLARE_TR_FUNCTIONS(TimelineMoveTool)
public: public:
explicit TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); explicit TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
void mousePressEvent(TimelineMovableAbstractItem *item, void mousePressEvent(TimelineMovableAbstractItem *item,
QGraphicsSceneMouseEvent *event) override; QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) override;

View File

@@ -505,7 +505,7 @@ TimelineKeyframeItem::TimelineKeyframeItem(TimelinePropertyItem *parent, const M
TimelineKeyframeItem::~TimelineKeyframeItem() TimelineKeyframeItem::~TimelineKeyframeItem()
{ {
timelineScene()->selectKeyframes(SelectionMode::Remove, {this}); abstractScrollGraphicsScene()->selectKeyframes(SelectionMode::Remove, {this});
} }
void TimelineKeyframeItem::updateFrame() void TimelineKeyframeItem::updateFrame()
@@ -555,8 +555,8 @@ void TimelineKeyframeItem::commitPosition(const QPointF &point)
void TimelineKeyframeItem::itemDoubleClicked() void TimelineKeyframeItem::itemDoubleClicked()
{ {
std::pair<qreal, qreal> timelineRange = {timelineScene()->currentTimeline().startKeyframe(), std::pair<qreal, qreal> timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(),
timelineScene()->currentTimeline().endKeyframe()}; timelineGraphicsScene()->currentTimeline().endKeyframe()};
editValue(m_frame, timelineRange, propertyItem()->propertyName()); editValue(m_frame, timelineRange, propertyItem()->propertyName());
} }
@@ -565,6 +565,11 @@ TimelineKeyframeItem *TimelineKeyframeItem::asTimelineKeyframeItem()
return this; return this;
} }
TimelineGraphicsScene *TimelineKeyframeItem::timelineGraphicsScene() const
{
return qobject_cast<TimelineGraphicsScene *>(abstractScrollGraphicsScene());
}
void TimelineKeyframeItem::blockUpdates() void TimelineKeyframeItem::blockUpdates()
{ {
s_blockUpdates = true; s_blockUpdates = true;
@@ -643,21 +648,21 @@ void TimelineKeyframeItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *even
QMenu mainMenu; QMenu mainMenu;
QAction *removeAction = mainMenu.addAction(tr("Delete Keyframe")); QAction *removeAction = mainMenu.addAction(tr("Delete Keyframe"));
QObject::connect(removeAction, &QAction::triggered, [this]() { QObject::connect(removeAction, &QAction::triggered, [this]() {
timelineScene()->handleKeyframeDeletion(); timelineGraphicsScene()->handleKeyframeDeletion();
}); });
QAction *editEasingAction = mainMenu.addAction(tr("Edit Easing Curve...")); QAction *editEasingAction = mainMenu.addAction(tr("Edit Easing Curve..."));
QObject::connect(editEasingAction, &QAction::triggered, [this]() { QObject::connect(editEasingAction, &QAction::triggered, [this]() {
const QList<ModelNode> keys = Utils::transform(timelineScene()->selectedKeyframes(), const QList<ModelNode> keys = Utils::transform(abstractScrollGraphicsScene()->selectedKeyframes(),
&TimelineKeyframeItem::m_frame); &TimelineKeyframeItem::m_frame);
setEasingCurve(timelineScene(), keys); setEasingCurve(timelineGraphicsScene(), keys);
}); });
QAction *editValueAction = mainMenu.addAction(tr("Edit Keyframe...")); QAction *editValueAction = mainMenu.addAction(tr("Edit Keyframe..."));
QObject::connect(editValueAction, &QAction::triggered, [this]() { QObject::connect(editValueAction, &QAction::triggered, [this]() {
std::pair<qreal, qreal> timelineRange = {timelineScene()->currentTimeline().startKeyframe(), std::pair<qreal, qreal> timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(),
timelineScene()->currentTimeline().endKeyframe()}; timelineGraphicsScene()->currentTimeline().endKeyframe()};
editValue(m_frame, timelineRange, propertyItem()->propertyName()); editValue(m_frame, timelineRange, propertyItem()->propertyName());
}); });

View File

@@ -67,6 +67,7 @@ public:
void itemDoubleClicked() override; void itemDoubleClicked() override;
TimelineKeyframeItem *asTimelineKeyframeItem() override; TimelineKeyframeItem *asTimelineKeyframeItem() override;
TimelineGraphicsScene *timelineGraphicsScene() const;
protected: protected:
bool hasManualBezier() const; bool hasManualBezier() const;

View File

@@ -1,4 +1,4 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
@@ -215,15 +215,7 @@ QVector<qreal> TimelineSectionItem::keyframePositions() const
return out; return out;
} }
QTransform rotatationTransform(qreal degrees) static QPixmap rotateby90(const QPixmap &pixmap)
{
QTransform transform;
transform.rotate(degrees);
return transform;
}
QPixmap rotateby90(const QPixmap &pixmap)
{ {
QImage sourceImage = pixmap.toImage(); QImage sourceImage = pixmap.toImage();
QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format()); QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format());
@@ -550,6 +542,13 @@ void TimelineRulerSectionItem::invalidateRulerSize(const QmlTimeline &timeline)
m_end = timeline.endKeyframe(); m_end = timeline.endKeyframe();
} }
void TimelineRulerSectionItem::invalidateRulerSize(const qreal length)
{
m_duration = length;
m_start = 0;
m_end = length;
}
void TimelineRulerSectionItem::setRulerScaleFactor(int scaling) void TimelineRulerSectionItem::setRulerScaleFactor(int scaling)
{ {
qreal blend = qreal(scaling) / 100.0; qreal blend = qreal(scaling) / 100.0;
@@ -627,10 +626,12 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi
static const QColor highlightColor = Theme::instance()->Theme::qmlDesignerButtonColor(); static const QColor highlightColor = Theme::instance()->Theme::qmlDesignerButtonColor();
static const QColor handleColor = Theme::getColor(Theme::QmlDesigner_HighlightColor); static const QColor handleColor = Theme::getColor(Theme::QmlDesigner_HighlightColor);
const int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene());
painter->save(); painter->save();
painter->save(); painter->save();
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
painter->translate(-timelineScene()->scrollOffset(), 0); painter->translate(-scrollOffset, 0);
painter->fillRect(TimelineConstants::sectionWidth, painter->fillRect(TimelineConstants::sectionWidth,
0, 0,
size().width() - TimelineConstants::sectionWidth, size().width() - TimelineConstants::sectionWidth,
@@ -666,11 +667,13 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi
const int height = size().height() - 1; const int height = size().height() - 1;
drawLine(painter, drawLine(painter,
TimelineConstants::sectionWidth + timelineScene()->scrollOffset() TimelineConstants::sectionWidth + scrollOffset
- TimelineConstants::timelineLeftOffset, - TimelineConstants::timelineLeftOffset,
height, height,
size().width() + timelineScene()->scrollOffset(), size().width() + scrollOffset,
height); height);
QFont font = painter->font(); QFont font = painter->font();
@@ -720,9 +723,12 @@ void TimelineRulerSectionItem::paintTicks(QPainter *painter)
m_frameTick = qreal(deltaLine); m_frameTick = qreal(deltaLine);
int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene());
int height = size().height(); int height = size().height();
const int totalWidth = (size().width() + timelineScene()->scrollOffset()) / m_scaling; const int totalWidth = (size().width() + scrollOffset) / m_scaling;
for (int i = timelineScene()->scrollOffset() / m_scaling; i < totalWidth; ++i) {
for (int i = scrollOffset / m_scaling; i < totalWidth; ++i) {
if ((i % deltaText) == 0) { if ((i % deltaText) == 0) {
drawCenteredText(painter, drawCenteredText(painter,
TimelineConstants::sectionWidth + i * m_scaling, TimelineConstants::sectionWidth + i * m_scaling,
@@ -794,11 +800,11 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end)
qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
- scrollOffset()); - scrollOffset());
qreal max = qreal(timelineScene()->rulerWidth() - TimelineConstants::sectionWidth qreal max = qreal(abstractScrollGraphicsScene()->rulerWidth() - TimelineConstants::sectionWidth
+ rect().width()); + rect().width());
const qreal minFrameX = mapFromFrameToScene(timelineScene()->startFrame()); const qreal minFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->startFrame() - 1);
const qreal maxFrameX = mapFromFrameToScene(timelineScene()->endFrame()); const qreal maxFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->endFrame()+ 1000);
if (min < minFrameX) if (min < minFrameX)
min = minFrameX; min = minFrameX;
@@ -811,7 +817,7 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end)
else else
dragHandle(rect(), end, min, max); dragHandle(rect(), end, min, max);
timelineScene()->statusBarMessageChanged( abstractScrollGraphicsScene()->statusBarMessageChanged(
tr("Range from %1 to %2") tr("Range from %1 to %2")
.arg(qRound(mapFromSceneToFrame(rect().x()))) .arg(qRound(mapFromSceneToFrame(rect().x())))
.arg(qRound(mapFromSceneToFrame(rect().width() + rect().x())))); .arg(qRound(mapFromSceneToFrame(rect().width() + rect().x()))));
@@ -975,7 +981,7 @@ void TimelineBarItem::dragCenter(QRectF rect, const QPointF &pos, qreal min, qre
if (validateBounds(pos.x() - rect.topLeft().x())) { if (validateBounds(pos.x() - rect.topLeft().x())) {
qreal targetX = pos.x() - m_pivot; qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame); targetX = mapFromFrameToScene(snappedTargetFrame);
} }
rect.moveLeft(targetX); rect.moveLeft(targetX);
@@ -999,7 +1005,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre
if (validateBounds(pos.x() - left.topLeft().x())) { if (validateBounds(pos.x() - left.topLeft().x())) {
qreal targetX = pos.x() - m_pivot; qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame); targetX = mapFromFrameToScene(snappedTargetFrame);
} }
rect.setLeft(targetX); rect.setLeft(targetX);
@@ -1015,7 +1021,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre
if (validateBounds(pos.x() - right.topRight().x())) { if (validateBounds(pos.x() - right.topRight().x())) {
qreal targetX = pos.x() - m_pivot; qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX)); qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame); targetX = mapFromFrameToScene(snappedTargetFrame);
} }
rect.setRight(targetX); rect.setRight(targetX);

View File

@@ -151,6 +151,7 @@ public:
static TimelineRulerSectionItem *create(QGraphicsScene *parentScene, TimelineItem *parent); static TimelineRulerSectionItem *create(QGraphicsScene *parentScene, TimelineItem *parent);
void invalidateRulerSize(const QmlTimeline &timeline); void invalidateRulerSize(const QmlTimeline &timeline);
void invalidateRulerSize(const qreal length);
void setRulerScaleFactor(int scaling); void setRulerScaleFactor(int scaling);

View File

@@ -38,7 +38,7 @@
namespace QmlDesigner { namespace QmlDesigner {
TimelineSelectionTool::TimelineSelectionTool(TimelineGraphicsScene *scene, TimelineSelectionTool::TimelineSelectionTool(AbstractScrollGraphicsScene *scene,
TimelineToolDelegate *delegate) TimelineToolDelegate *delegate)
: TimelineAbstractTool(scene, delegate) : TimelineAbstractTool(scene, delegate)
, m_selectionRect(new QGraphicsRectItem) , m_selectionRect(new QGraphicsRectItem)

View File

@@ -45,7 +45,7 @@ enum class SelectionMode { New, Add, Remove, Toggle };
class TimelineSelectionTool : public TimelineAbstractTool class TimelineSelectionTool : public TimelineAbstractTool
{ {
public: public:
explicit TimelineSelectionTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate); explicit TimelineSelectionTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
~TimelineSelectionTool() override; ~TimelineSelectionTool() override;

View File

@@ -56,19 +56,19 @@
namespace QmlDesigner { namespace QmlDesigner {
bool isSpacer(QObject *object) static bool isSpacer(QObject *object)
{ {
return object->property("spacer_widget").toBool(); return object->property("spacer_widget").toBool();
} }
QWidget *createSpacer() static QWidget *createSpacer()
{ {
QWidget *spacer = new QWidget(); QWidget *spacer = new QWidget();
spacer->setProperty("spacer_widget", true); spacer->setProperty("spacer_widget", true);
return spacer; return spacer;
} }
int controlWidth(QToolBar *bar, QObject *control) static int controlWidth(QToolBar *bar, QObject *control)
{ {
QWidget *widget = nullptr; QWidget *widget = nullptr;
@@ -84,7 +84,7 @@ int controlWidth(QToolBar *bar, QObject *control)
return 0; return 0;
} }
QAction *createAction(const Core::Id &id, static QAction *createAction(const Core::Id &id,
const QIcon &icon, const QIcon &icon,
const QString &name, const QString &name,
const QKeySequence &shortcut) const QKeySequence &shortcut)

View File

@@ -38,7 +38,7 @@
namespace QmlDesigner { namespace QmlDesigner {
TimelineToolDelegate::TimelineToolDelegate(TimelineGraphicsScene *scene) TimelineToolDelegate::TimelineToolDelegate(AbstractScrollGraphicsScene *scene)
: m_scene(scene) : m_scene(scene)
, m_start() , m_start()
, m_moveTool(new TimelineMoveTool(scene, this)) , m_moveTool(new TimelineMoveTool(scene, this))

View File

@@ -38,7 +38,7 @@ class TimelineGraphicsScene;
class TimelineToolDelegate class TimelineToolDelegate
{ {
public: public:
TimelineToolDelegate(TimelineGraphicsScene* scene); TimelineToolDelegate(AbstractScrollGraphicsScene* scene);
QPointF startPoint() const; QPointF startPoint() const;
@@ -65,7 +65,7 @@ private:
private: private:
static const int dragDistance = 20; static const int dragDistance = 20;
TimelineGraphicsScene* m_scene; AbstractScrollGraphicsScene* m_scene;
QPointF m_start; QPointF m_start;

View File

@@ -0,0 +1,35 @@
QT *= qml quick core
VPATH += $$PWD
INCLUDEPATH += $$PWD
SOURCES += \
transitioneditorview.cpp \
transitioneditorwidget.cpp \
transitioneditortoolbar.cpp \
transitioneditorgraphicsscene.cpp \
transitioneditorgraphicslayout.cpp \
transitioneditorsectionitem.cpp \
transitioneditorpropertyitem.cpp \
transitioneditorsettingsdialog.cpp \
transitionform.cpp
HEADERS += \
transitioneditorconstants \
transitioneditorview.h \
transitioneditorwidget.h \
transitioneditortoolbar.h \
transitioneditorgraphicsscene.h \
transitioneditorgraphicslayout.h \
transitioneditorsectionitem.h \
transitioneditorpropertyitem.h \
transitioneditorsettingsdialog.h \
transitionform.h
RESOURCES += \
transitioneditor.qrc
FORMS += \
transitioneditorsettingsdialog.ui \
transitionform.ui

View File

@@ -0,0 +1,4 @@
<RCC>
<qresource prefix="/transitioneditor">
</qresource>
</RCC>

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QGraphicsItem>
namespace QmlDesigner {
namespace TransitionEditorConstants {
const int transitionEditorSectionItemUserType = QGraphicsItem::UserType + 6;
const int transitionEditorPropertyItemUserType = QGraphicsItem::UserType + 7;
} // namespace TransitionEditorConstants
} // namespace QmlDesigner

View File

@@ -0,0 +1,172 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorgraphicslayout.h"
#include "timelinegraphicsscene.h"
#include "timelineplaceholder.h"
#include "timelinesectionitem.h"
#include "timelineview.h"
#include "transitioneditorsectionitem.h"
#include <QGraphicsLinearLayout>
#include <cmath>
namespace QmlDesigner {
TransitionEditorGraphicsLayout::TransitionEditorGraphicsLayout(QGraphicsScene *scene,
TimelineItem *parent)
: TimelineItem(parent)
, m_layout(new QGraphicsLinearLayout)
, m_rulerItem(TimelineRulerSectionItem::create(scene, this))
, m_placeholder1(TimelinePlaceholder::create(scene, this))
, m_placeholder2(TimelinePlaceholder::create(scene, this))
{
m_layout->setOrientation(Qt::Vertical);
m_layout->setSpacing(0);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->addItem(m_rulerItem);
m_layout->addItem(m_placeholder1);
m_layout->addItem(m_placeholder2);
setLayout(m_layout);
setPos(QPointF(0, 0));
connect(m_rulerItem,
&TimelineRulerSectionItem::rulerClicked,
this,
&TransitionEditorGraphicsLayout::rulerClicked);
}
TransitionEditorGraphicsLayout::~TransitionEditorGraphicsLayout() = default;
double TransitionEditorGraphicsLayout::rulerWidth() const
{
return m_rulerItem->preferredWidth();
}
double TransitionEditorGraphicsLayout::rulerScaling() const
{
return m_rulerItem->rulerScaling();
}
double TransitionEditorGraphicsLayout::rulerDuration() const
{
return m_rulerItem->rulerDuration();
}
double TransitionEditorGraphicsLayout::endFrame() const
{
return m_rulerItem->endFrame();
}
void TransitionEditorGraphicsLayout::setWidth(int width)
{
m_rulerItem->setSizeHints(width);
m_placeholder1->setMinimumWidth(width);
m_placeholder2->setMinimumWidth(width);
setPreferredWidth(width);
setMaximumWidth(width);
}
void TransitionEditorGraphicsLayout::setTransition(const ModelNode &transition)
{
m_layout->removeItem(m_rulerItem);
m_layout->removeItem(m_placeholder1);
m_layout->removeItem(m_placeholder2);
m_rulerItem->setParentItem(nullptr);
m_placeholder1->setParentItem(nullptr);
m_placeholder2->setParentItem(nullptr);
qDeleteAll(this->childItems());
m_rulerItem->setParentItem(this);
qreal duration = 2000;
if (transition.isValid() && transition.hasAuxiliaryData("transitionDuration"))
duration = transition.auxiliaryData("transitionDuration").toDouble();
setDuration(duration);
m_layout->addItem(m_rulerItem);
m_placeholder1->setParentItem(this);
m_layout->addItem(m_placeholder1);
m_layout->invalidate();
if (transition.isValid() && !transition.directSubModelNodes().isEmpty()) {
for (const ModelNode &parallel : transition.directSubModelNodes()) {
auto item = TransitionEditorSectionItem::create(parallel, this);
m_layout->addItem(item);
}
}
m_placeholder2->setParentItem(this);
m_layout->addItem(m_placeholder2);
if (auto *scene = timelineScene())
if (auto *view = scene->timelineView())
if (!transition.isValid() && view->isAttached())
emit scaleFactorChanged(0);
}
void TransitionEditorGraphicsLayout::setDuration(qreal duration)
{
m_rulerItem->invalidateRulerSize(duration);
}
void TransitionEditorGraphicsLayout::setRulerScaleFactor(int factor)
{
m_rulerItem->setRulerScaleFactor(factor);
}
void TransitionEditorGraphicsLayout::invalidate()
{
m_layout->invalidate();
}
int TransitionEditorGraphicsLayout::maximumScrollValue() const
{
const qreal w = this->geometry().width() - qreal(TimelineConstants::sectionWidth);
const qreal duration = m_rulerItem->rulerDuration() + m_rulerItem->rulerDuration() * 0.1;
const qreal maxr = m_rulerItem->rulerScaling() * duration - w;
return std::round(qMax(maxr, 0.0));
}
void TransitionEditorGraphicsLayout::activate()
{
m_layout->activate();
}
TimelineRulerSectionItem *TransitionEditorGraphicsLayout::ruler() const
{
return m_rulerItem;
}
} // End namespace QmlDesigner.

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "timelineitem.h"
QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout)
namespace QmlDesigner {
class TimelineItem;
class TimelineRulerSectionItem;
class TimelinePlaceholder;
class ModelNode;
class TransitionEditorGraphicsLayout : public TimelineItem
{
Q_OBJECT
signals:
void rulerClicked(const QPointF &pos);
void scaleFactorChanged(int factor);
public:
TransitionEditorGraphicsLayout(QGraphicsScene *scene, TimelineItem *parent = nullptr);
~TransitionEditorGraphicsLayout() override;
public:
double rulerWidth() const;
double rulerScaling() const;
double rulerDuration() const;
double endFrame() const;
void setWidth(int width);
void setTransition(const ModelNode &transition);
void setDuration(qreal duration);
void setRulerScaleFactor(int factor);
void invalidate();
int maximumScrollValue() const;
void activate();
TimelineRulerSectionItem *ruler() const;
private:
QGraphicsLinearLayout *m_layout = nullptr;
TimelineRulerSectionItem *m_rulerItem = nullptr;
TimelinePlaceholder *m_placeholder1 = nullptr;
TimelinePlaceholder *m_placeholder2 = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,431 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorgraphicsscene.h"
#include "transitioneditorgraphicslayout.h"
#include "transitioneditorpropertyitem.h"
#include "transitioneditorsectionitem.h"
#include "transitioneditortoolbar.h"
#include "transitioneditorview.h"
#include "transitioneditorwidget.h"
#include "timelineactions.h"
#include "timelineitem.h"
#include "timelinemovableabstractitem.h"
#include "timelinemovetool.h"
#include "timelineplaceholder.h"
#include "timelinepropertyitem.h"
#include "timelinesectionitem.h"
#include <designdocumentview.h>
#include <exception.h>
#include <rewritertransaction.h>
#include <rewriterview.h>
#include <viewmanager.h>
#include <qmldesignerplugin.h>
#include <qmlobjectnode.h>
#include <qmltimelinekeyframegroup.h>
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <variantproperty.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
#include <QApplication>
#include <QComboBox>
#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QKeyEvent>
#include <cmath>
namespace QmlDesigner {
static int deleteKey()
{
if (Utils::HostOsInfo::isMacHost())
return Qt::Key_Backspace;
return Qt::Key_Delete;
}
TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWidget *parent)
: AbstractScrollGraphicsScene(parent)
, m_parent(parent)
, m_layout(new TransitionEditorGraphicsLayout(this))
, m_tools(this)
{
addItem(m_layout);
setSceneRect(m_layout->geometry());
connect(m_layout, &QGraphicsWidget::geometryChanged, this, [this]() {
auto rect = m_layout->geometry();
setSceneRect(rect);
if (auto *gview = graphicsView())
gview->setSceneRect(rect.adjusted(0, TimelineConstants::rulerHeight, 0, 0));
if (auto *rview = rulerView())
rview->setSceneRect(rect);
});
auto changeScale = [this](int factor) {
transitionEditorWidget()->changeScaleFactor(factor);
setRulerScaling(qreal(factor));
};
connect(m_layout, &TransitionEditorGraphicsLayout::scaleFactorChanged, changeScale);
}
TransitionEditorGraphicsScene::~TransitionEditorGraphicsScene()
{
QSignalBlocker block(this);
qDeleteAll(items());
}
void TransitionEditorGraphicsScene::invalidateScrollbar()
{
double max = m_layout->maximumScrollValue();
transitionEditorWidget()->setupScrollbar(0, max, scrollOffset());
if (scrollOffset() > max)
setScrollOffset(max);
}
void TransitionEditorGraphicsScene::onShow()
{
emit m_layout->scaleFactorChanged(0);
}
void TransitionEditorGraphicsScene::setTransition(const ModelNode &transition)
{
clearSelection();
m_layout->setTransition(transition);
}
void TransitionEditorGraphicsScene::clearTransition()
{
m_transition = {};
m_layout->setTransition({});
}
void TransitionEditorGraphicsScene::setWidth(int width)
{
m_layout->setWidth(width);
invalidateScrollbar();
}
void TransitionEditorGraphicsScene::invalidateLayout()
{
m_layout->invalidate();
}
void TransitionEditorGraphicsScene::setDuration(int duration)
{
if (m_transition.isValid())
m_transition.setAuxiliaryData("transitionDuration", duration);
m_layout->setDuration(duration);
qreal scaling = m_layout->rulerScaling();
setRulerScaling(scaling);
}
qreal TransitionEditorGraphicsScene::rulerScaling() const
{
return m_layout->rulerScaling();
}
int TransitionEditorGraphicsScene::rulerWidth() const
{
return m_layout->rulerWidth();
}
qreal TransitionEditorGraphicsScene::rulerDuration() const
{
return m_layout->rulerDuration();
}
qreal TransitionEditorGraphicsScene::endFrame() const
{
return m_layout->endFrame();
}
qreal TransitionEditorGraphicsScene::startFrame() const
{
return 0;
}
qreal TransitionEditorGraphicsScene::mapToScene(qreal x) const
{
return TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
+ (x - startFrame()) * rulerScaling() - scrollOffset();
}
qreal TransitionEditorGraphicsScene::mapFromScene(qreal x) const
{
auto xPosOffset = (x - TimelineConstants::sectionWidth - TimelineConstants::timelineLeftOffset)
+ scrollOffset();
return xPosOffset / rulerScaling() + startFrame();
}
void TransitionEditorGraphicsScene::setRulerScaling(int scaleFactor)
{
m_layout->setRulerScaleFactor(scaleFactor);
setScrollOffset(0);
invalidateSections();
invalidateScrollbar();
update();
}
void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &target)
{
if (!target.isValid())
return;
bool found = false;
const QList<QGraphicsItem *> items = m_layout->childItems();
for (auto child : items)
TimelineSectionItem::updateDataForTarget(child, target, &found);
if (!found)
invalidateScene();
clearSelection();
invalidateLayout();
}
void TransitionEditorGraphicsScene::invalidateScene()
{
invalidateScrollbar();
}
void TransitionEditorGraphicsScene::invalidateCurrentValues()
{
const QList<QGraphicsItem *> constItems = items();
for (auto item : constItems)
TimelinePropertyItem::updateTextEdit(item);
}
QGraphicsView *TransitionEditorGraphicsScene::graphicsView() const
{
const QList<QGraphicsView *> constViews = views();
for (auto *v : constViews)
if (v->objectName() == "SceneView")
return v;
return nullptr;
}
QGraphicsView *TransitionEditorGraphicsScene::rulerView() const
{
const QList<QGraphicsView *> constViews = views();
for (auto *v : constViews)
if (v->objectName() == "RulerView")
return v;
return nullptr;
}
QRectF TransitionEditorGraphicsScene::selectionBounds() const
{
QRectF bbox;
return bbox;
}
void TransitionEditorGraphicsScene::clearSelection()
{
if (m_selectedProperty)
m_selectedProperty->update();
m_selectedProperty = nullptr;
AbstractScrollGraphicsScene::clearSelection();
}
QList<QGraphicsItem *> TransitionEditorGraphicsScene::itemsAt(const QPointF &pos)
{
QTransform transform;
if (auto *gview = graphicsView())
transform = gview->transform();
return items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder, transform);
}
void TransitionEditorGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mousePressEvent(topItem, event);
QGraphicsScene::mousePressEvent(event);
}
void TransitionEditorGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mouseMoveEvent(topItem, event);
QGraphicsScene::mouseMoveEvent(event);
}
void TransitionEditorGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
/* The tool has handle the event last. */
QGraphicsScene::mouseReleaseEvent(event);
m_tools.mouseReleaseEvent(topItem, event);
}
void TransitionEditorGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mouseDoubleClickEvent(topItem, event);
QGraphicsScene::mouseDoubleClickEvent(event);
}
void TransitionEditorGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
{
if (qgraphicsitem_cast<QGraphicsProxyWidget *>(focusItem())) {
keyEvent->ignore();
QGraphicsScene::keyPressEvent(keyEvent);
return;
}
if (keyEvent->modifiers().testFlag(Qt::ControlModifier)) {
QGraphicsScene::keyPressEvent(keyEvent);
} else {
switch (keyEvent->key()) {
case Qt::Key_Left:
emit scroll(TimelineUtils::Side::Left);
keyEvent->accept();
break;
case Qt::Key_Right:
emit scroll(TimelineUtils::Side::Right);
keyEvent->accept();
break;
default:
QGraphicsScene::keyPressEvent(keyEvent);
break;
}
}
}
void TransitionEditorGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
{
if (qgraphicsitem_cast<QGraphicsProxyWidget *>(focusItem())) {
keyEvent->ignore();
QGraphicsScene::keyReleaseEvent(keyEvent);
return;
}
QGraphicsScene::keyReleaseEvent(keyEvent);
}
void TransitionEditorGraphicsScene::invalidateSections()
{
const QList<QGraphicsItem *> children = m_layout->childItems();
for (auto child : children)
TransitionEditorSectionItem::updateData(child);
clearSelection();
invalidateLayout();
}
TransitionEditorView *TransitionEditorGraphicsScene::transitionEditorView() const
{
return m_parent->transitionEditorView();
}
TransitionEditorWidget *TransitionEditorGraphicsScene::transitionEditorWidget() const
{
return m_parent;
}
TransitionEditorToolBar *TransitionEditorGraphicsScene::toolBar() const
{
return transitionEditorWidget()->toolBar();
}
void TransitionEditorGraphicsScene::activateLayout()
{
m_layout->activate();
}
AbstractView *TransitionEditorGraphicsScene::abstractView() const
{
return transitionEditorView();
}
bool TransitionEditorGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent *>(event)->key() == deleteKey()) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
}
Q_FALLTHROUGH();
default:
return QGraphicsScene::event(event);
}
}
ModelNode TransitionEditorGraphicsScene::transitionModelNode() const
{
if (transitionEditorView()->isAttached()) {
const QString timelineId = transitionEditorWidget()->toolBar()->currentTransitionId();
return transitionEditorView()->modelNodeForId(timelineId);
}
return ModelNode();
}
TransitionEditorPropertyItem *TransitionEditorGraphicsScene::selectedPropertyItem() const
{
return m_selectedProperty;
}
void TransitionEditorGraphicsScene::setSelectedPropertyItem(TransitionEditorPropertyItem *item)
{
if (m_selectedProperty)
m_selectedProperty->update();
m_selectedProperty = item;
emit selectionChanged();
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,146 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <timelineeditor/timelinegraphicsscene.h>
#include <qmltimeline.h>
#include <QGraphicsScene>
#include <memory>
QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout)
QT_FORWARD_DECLARE_CLASS(QComboBox)
namespace QmlDesigner {
class TransitionEditorView;
class TransitionEditorWidget;
class TransitionEditorToolBar;
class TransitionEditorGraphicsLayout;
class TimelineRulerSectionItem;
class TimelineFrameHandle;
class TimelineAbstractTool;
class TimelineMoveTool;
class TimelineKeyframeItem;
class TimelinePlaceholder;
class TimelineToolBar;
class TransitionEditorPropertyItem;
class TransitionEditorGraphicsScene : public AbstractScrollGraphicsScene
{
Q_OBJECT
signals:
void selectionChanged();
void scroll(const TimelineUtils::Side &side);
public:
explicit TransitionEditorGraphicsScene(TransitionEditorWidget *parent);
~TransitionEditorGraphicsScene() override;
void onShow();
void setTransition(const ModelNode &transition);
void clearTransition();
void setWidth(int width);
void invalidateLayout();
void setDuration(int duration);
TransitionEditorView *transitionEditorView() const;
TransitionEditorWidget *transitionEditorWidget() const;
TransitionEditorToolBar *toolBar() const;
qreal rulerScaling() const override;
int rulerWidth() const override;
qreal rulerDuration() const override;
qreal endFrame() const override;
qreal startFrame() const override;
qreal mapToScene(qreal x) const;
qreal mapFromScene(qreal x) const;
void setRulerScaling(int scaling);
void invalidateSectionForTarget(const ModelNode &modelNode);
void invalidateScene();
void invalidateCurrentValues();
void invalidateRecordButtonsStatus();
QGraphicsView *graphicsView() const;
QGraphicsView *rulerView() const;
QRectF selectionBounds() const;
void selectKeyframes(const SelectionMode &mode, const QList<TimelineKeyframeItem *> &items);
void clearSelection() override;
void activateLayout();
AbstractView *abstractView() const override;
ModelNode transitionModelNode() const;
TransitionEditorPropertyItem *selectedPropertyItem() const;
void setSelectedPropertyItem(TransitionEditorPropertyItem *item);
void invalidateScrollbar() override;
signals:
void statusBarMessageChanged(const QString &message);
protected:
bool event(QEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void keyPressEvent(QKeyEvent *keyEvent) override;
void keyReleaseEvent(QKeyEvent *keyEvent) override;
private:
void invalidateSections();
QList<QGraphicsItem *> itemsAt(const QPointF &pos);
private:
TransitionEditorWidget *m_parent = nullptr;
TransitionEditorGraphicsLayout *m_layout = nullptr;
ModelNode m_transition;
int m_scrollOffset = 0;
TimelineToolDelegate m_tools;
TransitionEditorPropertyItem *m_selectedProperty = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,237 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorpropertyitem.h"
#include "abstractview.h"
#include "timelineconstants.h"
#include "timelineicons.h"
#include "transitioneditorgraphicsscene.h"
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <rewritertransaction.h>
#include <rewritingexception.h>
#include <theme.h>
#include <variantproperty.h>
#include <qmlobjectnode.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <coreplugin/icore.h>
#include <QCursor>
#include <QGraphicsProxyWidget>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QLineEdit>
#include <QMenu>
#include <QPainter>
#include <algorithm>
namespace QmlDesigner {
TransitionEditorPropertyItem *TransitionEditorPropertyItem::create(
const ModelNode &animation, TransitionEditorSectionItem *parent)
{
auto item = new TransitionEditorPropertyItem(parent);
item->m_animation = animation;
auto sectionItem = new QGraphicsWidget(item);
sectionItem->setGeometry(0,
0,
TimelineConstants::sectionWidth,
TimelineConstants::sectionHeight);
sectionItem->setZValue(10);
sectionItem->setCursor(Qt::ArrowCursor);
item->setToolTip(item->propertyName());
item->resize(parent->size());
item->m_barItem = new TransitionEditorBarItem(item);
item->invalidateBar();
return item;
}
int TransitionEditorPropertyItem::type() const
{
return Type;
}
void TransitionEditorPropertyItem::updateData()
{
invalidateBar();
}
void TransitionEditorPropertyItem::updateParentData()
{
TransitionEditorSectionItem::invalidateBar(parentItem());
}
bool TransitionEditorPropertyItem::isSelected() const
{
return transitionEditorGraphicsScene()->selectedPropertyItem() == this;
}
QString TransitionEditorPropertyItem::propertyName() const
{
if (m_animation.isValid()) {
const QString propertyName = m_animation.variantProperty("property").value().toString();
if (!propertyName.isEmpty())
return propertyName;
return m_animation.variantProperty("properties").value().toString();
}
return QString();
}
void TransitionEditorPropertyItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem *,
QWidget *)
{
painter->save();
static const QColor penColor = Theme::instance()->qmlDesignerBackgroundColorDarker();
static const QColor textColor = Theme::getColor(Theme::PanelTextColorLight);
static const QColor backgroundColor = Theme::instance()
->qmlDesignerBackgroundColorDarkAlternate();
painter->fillRect(0, 0, TimelineConstants::sectionWidth, size().height(), backgroundColor);
painter->fillRect(TimelineConstants::textIndentationProperties - 4,
0,
TimelineConstants::sectionWidth - TimelineConstants::textIndentationProperties
+ 4,
size().height(),
backgroundColor.darker(110));
painter->setPen(penColor);
drawLine(painter,
TimelineConstants::sectionWidth - 1,
0,
TimelineConstants::sectionWidth - 1,
size().height());
drawLine(painter,
TimelineConstants::textIndentationProperties - 4,
TimelineConstants::sectionHeight - 1,
size().width(),
TimelineConstants::sectionHeight - 1);
painter->setPen(textColor);
const QFontMetrics metrics(font());
const QString elidedText = metrics.elidedText(propertyName(),
Qt::ElideMiddle,
qreal(TimelineConstants::sectionWidth) * 2.0 / 3
- TimelineConstants::textIndentationProperties,
0);
painter->drawText(TimelineConstants::textIndentationProperties, 12, elidedText);
painter->restore();
}
void TransitionEditorPropertyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent * /*event */) {}
TransitionEditorPropertyItem::TransitionEditorPropertyItem(TransitionEditorSectionItem *parent)
: TimelineItem(parent)
{
setPreferredHeight(TimelineConstants::sectionHeight);
setMinimumHeight(TimelineConstants::sectionHeight);
setMaximumHeight(TimelineConstants::sectionHeight);
}
TransitionEditorGraphicsScene *TransitionEditorPropertyItem::transitionEditorGraphicsScene() const
{
return qobject_cast<TransitionEditorGraphicsScene *>(scene());
}
void TransitionEditorPropertyItem::invalidateBar()
{
qreal min = 0;
qreal max = 0;
QTC_ASSERT(m_animation.isValid(), return );
QTC_ASSERT(m_animation.hasParentProperty(), return );
const ModelNode parent = m_animation.parentProperty().parentModelNode();
for (const ModelNode &child : parent.directSubModelNodes())
if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation"))
min = child.variantProperty("duration").value().toDouble();
max = m_animation.variantProperty("duration").value().toDouble() + min;
const qreal sceneMin = m_barItem->mapFromFrameToScene(min);
QRectF barRect(sceneMin,
0,
(max - min) * m_barItem->rulerScaling(),
TimelineConstants::sectionHeight - 1);
m_barItem->setRect(barRect);
}
AbstractView *TransitionEditorPropertyItem::view() const
{
return m_animation.view();
}
ModelNode TransitionEditorPropertyItem::propertyAnimation() const
{
return m_animation;
}
ModelNode TransitionEditorPropertyItem::pauseAnimation() const
{
QTC_ASSERT(m_animation.isValid(), return {});
QTC_ASSERT(m_animation.hasParentProperty(), return {});
const ModelNode parent = m_animation.parentProperty().parentModelNode();
for (const ModelNode &child : parent.directSubModelNodes())
if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation"))
return child;
return {};
}
void TransitionEditorPropertyItem::select()
{
transitionEditorGraphicsScene()->setSelectedPropertyItem(this);
m_barItem->update();
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "transitioneditorsectionitem.h"
#include <modelnode.h>
#include <QGraphicsRectItem>
QT_FORWARD_DECLARE_CLASS(QLineEdit)
namespace QmlDesigner {
class TransitionEditorGraphicsScene;
class TransitionEditorPropertyItem : public TimelineItem
{
Q_OBJECT
public:
enum { Type = TransitionEditorConstants::transitionEditorPropertyItemUserType };
static TransitionEditorPropertyItem *create(const ModelNode &animation,
TransitionEditorSectionItem *parent = nullptr);
int type() const override;
void updateData();
void updateParentData();
bool isSelected() const;
QString propertyName() const;
void invalidateBar();
AbstractView *view() const;
ModelNode propertyAnimation() const;
ModelNode pauseAnimation() const;
void select();
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
private:
TransitionEditorPropertyItem(TransitionEditorSectionItem *parent = nullptr);
TransitionEditorGraphicsScene *transitionEditorGraphicsScene() const;
ModelNode m_animation;
TransitionEditorBarItem *m_barItem;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,803 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorsectionitem.h"
#include "transitioneditorgraphicsscene.h"
#include "transitioneditorpropertyitem.h"
#include "timelineactions.h"
#include "timelineconstants.h"
#include "timelineicons.h"
#include "timelinepropertyitem.h"
#include "timelineutils.h"
#include <abstractview.h>
#include <bindingproperty.h>
#include <variantproperty.h>
#include <qmltimeline.h>
#include <qmltimelinekeyframegroup.h>
#include <rewritingexception.h>
#include <theme.h>
#include <utils/qtcassert.h>
#include <QAction>
#include <QApplication>
#include <QColorDialog>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QMenu>
#include <QPainter>
#include <QPainterPath>
#include <QGraphicsView>
#include <QDebug>
#include <cmath>
#include <limits>
namespace QmlDesigner {
static void scaleDuration(const ModelNode &node, qreal s)
{
if (node.hasVariantProperty("duration")) {
qreal old = node.variantProperty("duration").value().toDouble();
node.variantProperty("duration").setValue(qRound(old * s));
}
}
static void moveDuration(const ModelNode &node, qreal s)
{
if (node.hasVariantProperty("duration")) {
qreal old = node.variantProperty("duration").value().toDouble();
node.variantProperty("duration").setValue(old + s);
}
}
class ClickDummy : public TimelineItem
{
public:
explicit ClickDummy(TransitionEditorSectionItem *parent)
: TimelineItem(parent)
{
setGeometry(0, 0, TimelineConstants::sectionWidth, TimelineConstants::sectionHeight);
setZValue(10);
setCursor(Qt::ArrowCursor);
}
protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
{
scene()->sendEvent(parentItem(), event);
}
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
{
scene()->sendEvent(parentItem(), event);
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override
{
scene()->sendEvent(parentItem(), event);
}
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
{
scene()->sendEvent(parentItem(), event);
}
};
TransitionEditorSectionItem::TransitionEditorSectionItem(TimelineItem *parent)
: TimelineItem(parent)
{}
TransitionEditorSectionItem *TransitionEditorSectionItem::create(const ModelNode &animation,
TimelineItem *parent)
{
auto item = new TransitionEditorSectionItem(parent);
ModelNode target;
if (animation.isValid()) {
const QList<ModelNode> propertyAnimations = animation.subModelNodesOfType(
"QtQuick.PropertyAnimation");
for (const ModelNode &child : propertyAnimations) {
if (child.hasBindingProperty("target"))
target = child.bindingProperty("target").resolveToModelNode();
}
}
item->m_targetNode = target;
item->m_animationNode = animation;
item->createPropertyItems();
if (target.isValid())
item->setToolTip(target.id());
item->m_dummyItem = new ClickDummy(item);
item->m_dummyItem->update();
item->m_barItem = new TransitionEditorBarItem(item);
item->invalidateBar();
item->invalidateHeight();
return item;
}
void TransitionEditorSectionItem::invalidateBar()
{
qreal min = std::numeric_limits<qreal>::max();
qreal max = 0;
if (!m_animationNode.isValid())
return;
for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) {
qreal locMin = 0;
qreal locMax = 0;
for (const ModelNode &child : sequential.directSubModelNodes()) {
if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PropertyAnimation"))
locMax = child.variantProperty("duration").value().toDouble();
else if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation"))
locMin = child.variantProperty("duration").value().toDouble();
}
locMax = locMax + locMin;
min = qMin(min, locMin);
max = qMax(max, locMax);
}
const qreal sceneMin = m_barItem->mapFromFrameToScene(min);
QRectF barRect(sceneMin,
0,
(max - min) * m_barItem->rulerScaling(),
TimelineConstants::sectionHeight - 1);
m_barItem->setRect(barRect);
}
int TransitionEditorSectionItem::type() const
{
return Type;
}
void TransitionEditorSectionItem::updateData(QGraphicsItem *item)
{
if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item))
sectionItem->updateData();
}
void TransitionEditorSectionItem::invalidateBar(QGraphicsItem *item)
{
if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item))
sectionItem->invalidateBar();
}
void TransitionEditorSectionItem::updateDataForTarget(QGraphicsItem *item,
const ModelNode &target,
bool *b)
{
if (!target.isValid())
return;
if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item)) {
if (sectionItem->m_targetNode == target) { //TODO update animation node
sectionItem->updateData();
if (b)
*b = true;
}
}
}
void TransitionEditorSectionItem::moveAllDurations(qreal offset)
{
for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) {
for (const ModelNode &child : sequential.directSubModelNodes()) {
if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PauseAnimation"))
moveDuration(child, offset);
}
}
}
void TransitionEditorSectionItem::scaleAllDurations(qreal scale)
{
for (const ModelNode &sequential : m_animationNode.directSubModelNodes()) {
for (const ModelNode &child : sequential.directSubModelNodes()) {
if (child.hasMetaInfo() && child.isSubclassOf("QtQuick.PropertyAnimation"))
scaleDuration(child, scale);
}
}
}
qreal TransitionEditorSectionItem::firstFrame()
{
return 0;
//if (!m_timeline.isValid())
//return 0;
//return m_timeline.minActualKeyframe(m_targetNode);
}
AbstractView *TransitionEditorSectionItem::view() const
{
return m_animationNode.view();
}
bool TransitionEditorSectionItem::isSelected() const
{
return m_targetNode.isValid() && m_targetNode.isSelected();
}
ModelNode TransitionEditorSectionItem::targetNode() const
{
return m_targetNode;
}
static QPixmap rotateby90(const QPixmap &pixmap)
{
QImage sourceImage = pixmap.toImage();
QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format());
for (int x = 0; x < pixmap.width(); x++)
for (int y = 0; y < pixmap.height(); y++)
destImage.setPixel(y, x, sourceImage.pixel(x, y));
QPixmap result = QPixmap::fromImage(destImage);
result.setDevicePixelRatio(pixmap.devicePixelRatio());
return result;
}
static int devicePixelHeight(const QPixmap &pixmap)
{
return pixmap.height() / pixmap.devicePixelRatioF();
}
void TransitionEditorSectionItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem * /*option*/,
QWidget *)
{
if (m_targetNode.isValid()) {
painter->save();
const QColor textColor = Theme::getColor(Theme::PanelTextColorLight);
const QColor penColor = Theme::instance()->qmlDesignerBackgroundColorDarker();
QColor brushColor = Theme::getColor(Theme::BackgroundColorDark);
int fillOffset = 0;
if (isSelected()) {
brushColor = Theme::getColor(Theme::QmlDesigner_HighlightColor);
fillOffset = 1;
}
painter->fillRect(0,
0,
TimelineConstants::sectionWidth,
TimelineConstants::sectionHeight - fillOffset,
brushColor);
painter->fillRect(TimelineConstants::sectionWidth,
0,
size().width() - TimelineConstants::sectionWidth,
size().height(),
Theme::instance()->qmlDesignerBackgroundColorDarkAlternate());
painter->setPen(penColor);
drawLine(painter,
TimelineConstants::sectionWidth - 1,
0,
TimelineConstants::sectionWidth - 1,
size().height() - 1);
drawLine(painter,
TimelineConstants::sectionWidth,
TimelineConstants::sectionHeight - 1,
size().width(),
TimelineConstants::sectionHeight - 1);
static const QPixmap arrow = Theme::getPixmap("down-arrow");
static const QPixmap arrow90 = rotateby90(arrow);
const QPixmap rotatedArrow = collapsed() ? arrow90 : arrow;
const int textOffset = QFontMetrics(font()).ascent()
+ (TimelineConstants::sectionHeight - QFontMetrics(font()).height())
/ 2;
painter->drawPixmap(collapsed() ? 6 : 4,
(TimelineConstants::sectionHeight - devicePixelHeight(rotatedArrow)) / 2,
rotatedArrow);
painter->setPen(textColor);
QFontMetrics fm(painter->font());
const QString elidedId = fm.elidedText(m_targetNode.id(),
Qt::ElideMiddle,
TimelineConstants::sectionWidth
- TimelineConstants::textIndentationSections);
painter->drawText(TimelineConstants::textIndentationSections, textOffset, elidedId);
painter->restore();
}
}
void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (event->pos().y() > TimelineConstants::sectionHeight
|| event->pos().x() < TimelineConstants::textIndentationSections) {
TimelineItem::mouseDoubleClickEvent(event);
return;
}
if (event->button() == Qt::LeftButton) {
event->accept();
toggleCollapsed();
}
}
void TransitionEditorSectionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->pos().y() > TimelineConstants::sectionHeight) {
TimelineItem::mousePressEvent(event);
return;
}
if (event->button() == Qt::LeftButton)
event->accept();
}
void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->pos().y() > TimelineConstants::sectionHeight) {
TimelineItem::mouseReleaseEvent(event);
return;
}
if (event->button() != Qt::LeftButton)
return;
event->accept();
if (event->pos().x() > TimelineConstants::textIndentationSections
&& event->button() == Qt::LeftButton) {
if (m_targetNode.isValid())
m_targetNode.view()->setSelectedModelNode(m_targetNode);
} else {
toggleCollapsed();
}
update();
}
void TransitionEditorSectionItem::resizeEvent(QGraphicsSceneResizeEvent *event)
{
TimelineItem::resizeEvent(event);
for (auto child : propertyItems()) {
TransitionEditorPropertyItem *item = static_cast<TransitionEditorPropertyItem *>(child);
item->resize(size().width(), TimelineConstants::sectionHeight);
}
}
void TransitionEditorSectionItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *) {}
void TransitionEditorSectionItem::updateData()
{
invalidateBar();
resize(rulerWidth(), size().height());
invalidateProperties();
update();
}
const QList<QGraphicsItem *> TransitionEditorSectionItem::propertyItems() const
{
QList<QGraphicsItem *> list;
const QList<QGraphicsItem *> children = childItems();
for (auto child : children) {
if (m_barItem != child && m_dummyItem != child)
list.append(child);
}
return list;
}
void TransitionEditorSectionItem::invalidateHeight()
{
int height = 0;
bool visible = true;
if (collapsed()) {
height = TimelineConstants::sectionHeight;
visible = false;
} else {
const QList<ModelNode> propertyAnimations = m_animationNode.subModelNodesOfType(
"QtQuick.PropertyAnimation");
height = TimelineConstants::sectionHeight
+ propertyAnimations.count() * TimelineConstants::sectionHeight;
visible = true;
}
for (auto child : propertyItems())
child->setVisible(visible);
setPreferredHeight(height);
setMinimumHeight(height);
setMaximumHeight(height);
auto transitionScene = qobject_cast<TransitionEditorGraphicsScene *>(scene());
transitionScene->activateLayout();
}
void TransitionEditorSectionItem::createPropertyItems()
{
int yPos = TimelineConstants::sectionHeight;
const QList<ModelNode> propertyAnimations = m_animationNode.subModelNodesOfType(
"QtQuick.PropertyAnimation");
for (const auto &anim : propertyAnimations) {
auto item = TransitionEditorPropertyItem::create(anim, this);
item->setY(yPos);
yPos = yPos + TimelineConstants::sectionHeight;
}
}
void TransitionEditorSectionItem::invalidateProperties()
{
for (auto child : propertyItems()) {
delete child;
}
createPropertyItems();
for (auto child : propertyItems()) {
TransitionEditorPropertyItem *item = static_cast<TransitionEditorPropertyItem *>(child);
item->updateData();
item->resize(size().width(), TimelineConstants::sectionHeight);
}
invalidateHeight();
}
bool TransitionEditorSectionItem::collapsed() const
{
return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded");
}
qreal TransitionEditorSectionItem::rulerWidth() const
{
return static_cast<TimelineGraphicsScene *>(scene())->rulerWidth();
}
void TransitionEditorSectionItem::toggleCollapsed()
{
QTC_ASSERT(m_targetNode.isValid(), return );
if (collapsed())
m_targetNode.setAuxiliaryData("timeline_expanded", true);
else
m_targetNode.removeAuxiliaryData("timeline_expanded");
invalidateHeight();
}
TransitionEditorBarItem::TransitionEditorBarItem(TransitionEditorSectionItem *parent)
: TimelineMovableAbstractItem(parent)
{
setAcceptHoverEvents(true);
setPen(Qt::NoPen);
}
TransitionEditorBarItem::TransitionEditorBarItem(TransitionEditorPropertyItem *parent)
: TimelineMovableAbstractItem(parent)
{
setAcceptHoverEvents(true);
setPen(Qt::NoPen);
}
void TransitionEditorBarItem::itemMoved(const QPointF &start, const QPointF &end)
{
if (isActiveHandle(Location::Undefined))
dragInit(rect(), start);
qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
- scrollOffset());
qreal max = qreal(abstractScrollGraphicsScene()->rulerWidth() - TimelineConstants::sectionWidth
+ rect().width());
const qreal minFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->startFrame());
const qreal maxFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->endFrame());
if (min < minFrameX)
min = minFrameX;
if (max > maxFrameX)
max = maxFrameX;
if (isActiveHandle(Location::Center))
dragCenter(rect(), end, min, max);
else
dragHandle(rect(), end, min, max);
emit abstractScrollGraphicsScene()->statusBarMessageChanged(
tr("Range from %1 to %2")
.arg(qRound(mapFromSceneToFrame(rect().x())))
.arg(qRound(mapFromSceneToFrame(rect().width() + rect().x()))));
}
void TransitionEditorBarItem::commitPosition(const QPointF & /*point*/)
{
if (sectionItem() && sectionItem()->view()) {
if (m_handle != Location::Undefined) {
sectionItem()
->view()
->executeInTransaction("TransitionEditorBarItem::commitPosition", [this]() {
qreal scaleFactor = rect().width() / m_oldRect.width();
qreal moved = (rect().topLeft().x() - m_oldRect.topLeft().x()) / rulerScaling();
qreal supposedFirstFrame = qRound(moved);
sectionItem()->scaleAllDurations(scaleFactor);
sectionItem()->moveAllDurations(supposedFirstFrame);
sectionItem()->updateData();
});
}
} else if (propertyItem() && propertyItem()->view()) {
if (m_handle != Location::Undefined) {
propertyItem()
->view()
->executeInTransaction("TransitionEditorBarItem::commitPosition", [this]() {
qreal scaleFactor = rect().width() / m_oldRect.width();
qreal moved = (rect().topLeft().x() - m_oldRect.topLeft().x()) / rulerScaling();
qreal supposedFirstFrame = qRound(moved);
scaleDuration(propertyItem()->propertyAnimation(), scaleFactor);
moveDuration(propertyItem()->pauseAnimation(), supposedFirstFrame);
propertyItem()->updateData();
propertyItem()->updateParentData();
});
}
}
m_handle = Location::Undefined;
m_bounds = Location::Undefined;
m_pivot = 0.0;
m_oldRect = QRectF();
scrollOffsetChanged();
}
void TransitionEditorBarItem::scrollOffsetChanged()
{
if (sectionItem())
sectionItem()->invalidateBar();
else if (propertyItem())
propertyItem()->invalidateBar();
}
void TransitionEditorBarItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
QColor brushColor = Theme::getColor(Theme::QmlDesigner_HighlightColor);
QColor brushColorSection = Theme::getColor(Theme::QmlDesigner_HighlightColor).darker(120);
QColor penColor = Theme::getColor(Theme::QmlDesigner_HighlightColor).lighter(140);
const QRectF itemRect = rect();
painter->save();
painter->setClipRect(TimelineConstants::sectionWidth,
0,
itemRect.width() + itemRect.x(),
itemRect.height());
if (sectionItem())
painter->fillRect(itemRect, brushColorSection);
else
painter->fillRect(itemRect, brushColor);
if (propertyItem() && propertyItem()->isSelected()) {
painter->setPen(penColor);
painter->drawRect(itemRect.adjusted(0, 0, 0, -1));
}
painter->restore();
}
void TransitionEditorBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
const auto p = event->pos();
QRectF left, right;
if (handleRects(rect(), left, right)) {
if (left.contains(p) || right.contains(p)) {
if (cursor().shape() != Qt::SizeHorCursor)
setCursor(QCursor(Qt::SizeHorCursor));
} else if (rect().contains(p)) {
if (cursor().shape() != Qt::ClosedHandCursor)
setCursor(QCursor(Qt::ClosedHandCursor));
}
} else {
if (rect().contains(p))
setCursor(QCursor(Qt::ClosedHandCursor));
}
}
void TransitionEditorBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent * /*event*/) {}
void TransitionEditorBarItem::mousePressEvent(QGraphicsSceneMouseEvent * /*event*/)
{
if (propertyItem())
propertyItem()->select();
}
TransitionEditorSectionItem *TransitionEditorBarItem::sectionItem() const
{
return qgraphicsitem_cast<TransitionEditorSectionItem *>(parentItem());
}
TransitionEditorPropertyItem *TransitionEditorBarItem::propertyItem() const
{
return qgraphicsitem_cast<TransitionEditorPropertyItem *>(parentItem());
}
void TransitionEditorBarItem::dragInit(const QRectF &rect, const QPointF &pos)
{
QRectF left, right;
m_oldRect = rect;
if (handleRects(rect, left, right)) {
if (left.contains(pos)) {
m_handle = Location::Left;
m_pivot = pos.x() - left.topLeft().x();
} else if (right.contains(pos)) {
m_handle = Location::Right;
m_pivot = pos.x() - right.topRight().x();
} else if (rect.contains(pos)) {
m_handle = Location::Center;
m_pivot = pos.x() - rect.topLeft().x();
}
} else {
if (rect.contains(pos)) {
m_handle = Location::Center;
m_pivot = pos.x() - rect.topLeft().x();
}
}
}
void TransitionEditorBarItem::dragCenter(QRectF rect, const QPointF &pos, qreal min, qreal max)
{
if (validateBounds(pos.x() - rect.topLeft().x())) {
qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame);
}
rect.moveLeft(targetX);
if (rect.topLeft().x() < min) {
rect.moveLeft(min);
setOutOfBounds(Location::Left);
} else if (rect.topRight().x() > max) {
rect.moveRight(max);
setOutOfBounds(Location::Right);
}
setRect(rect);
}
}
void TransitionEditorBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qreal max)
{
QRectF left, right;
handleRects(rect, left, right);
if (isActiveHandle(Location::Left)) {
if (validateBounds(pos.x() - left.topLeft().x())) {
qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame);
}
rect.setLeft(targetX);
if (rect.left() < min) {
rect.setLeft(min);
setOutOfBounds(Location::Left);
} else if (rect.left() >= rect.right() - minimumBarWidth)
rect.setLeft(rect.right() - minimumBarWidth);
setRect(rect);
}
} else if (isActiveHandle(Location::Right)) {
if (validateBounds(pos.x() - right.topRight().x())) {
qreal targetX = pos.x() - m_pivot;
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
targetX = mapFromFrameToScene(snappedTargetFrame);
}
rect.setRight(targetX);
if (rect.right() > max) {
rect.setRight(max);
setOutOfBounds(Location::Right);
} else if (rect.right() <= rect.left() + minimumBarWidth)
rect.setRight(rect.left() + minimumBarWidth);
setRect(rect);
}
}
}
bool TransitionEditorBarItem::handleRects(const QRectF &rect, QRectF &left, QRectF &right) const
{
if (rect.width() < minimumBarWidth)
return false;
const qreal handleSize = rect.height();
auto handleRect = QRectF(0, 0, handleSize, handleSize);
handleRect.moveCenter(rect.center());
handleRect.moveLeft(rect.left());
left = handleRect;
handleRect.moveRight(rect.right());
right = handleRect;
return true;
}
bool TransitionEditorBarItem::isActiveHandle(Location location) const
{
return m_handle == location;
}
void TransitionEditorBarItem::setOutOfBounds(Location location)
{
m_bounds = location;
update();
}
bool TransitionEditorBarItem::validateBounds(qreal distance)
{
update();
if (m_bounds == Location::Left) {
if (distance > m_pivot)
m_bounds = Location::Center;
return false;
} else if (m_bounds == Location::Right) {
if (distance < m_pivot)
m_bounds = Location::Center;
return false;
}
return true;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,145 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "transitioneditorconstants.h"
#include <timelineeditor/timelineitem.h>
#include <timelineeditor/timelinemovableabstractitem.h>
#include <modelnode.h>
#include <qmltimeline.h>
QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QPainter)
namespace QmlDesigner {
class TransitionEditorSectionItem;
class TransitionEditorPropertyItem;
class TransitionEditorBarItem : public TimelineMovableAbstractItem
{
Q_DECLARE_TR_FUNCTIONS(TimelineBarItem)
enum class Location { Undefined, Center, Left, Right };
public:
explicit TransitionEditorBarItem(TransitionEditorSectionItem *parent);
explicit TransitionEditorBarItem(TransitionEditorPropertyItem *parent);
void itemMoved(const QPointF &start, const QPointF &end) override;
void commitPosition(const QPointF &point) override;
protected:
void scrollOffsetChanged() override;
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget = nullptr) override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *) override;
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
private:
TransitionEditorSectionItem *sectionItem() const;
TransitionEditorPropertyItem *propertyItem() const;
void dragInit(const QRectF &rect, const QPointF &pos);
void dragCenter(QRectF rect, const QPointF &pos, qreal min, qreal max);
void dragHandle(QRectF rect, const QPointF &pos, qreal min, qreal max);
bool handleRects(const QRectF &rect, QRectF &left, QRectF &right) const;
bool isActiveHandle(Location location) const;
void setOutOfBounds(Location location);
bool validateBounds(qreal pivot);
private:
Location m_handle = Location::Undefined;
Location m_bounds = Location::Undefined;
qreal m_pivot = 0.0;
QRectF m_oldRect;
static constexpr qreal minimumBarWidth = 2.0
* static_cast<qreal>(TimelineConstants::sectionHeight);
};
class TransitionEditorSectionItem : public TimelineItem
{
Q_OBJECT
public:
enum { Type = TransitionEditorConstants::transitionEditorSectionItemUserType };
static TransitionEditorSectionItem *create(const ModelNode &animation,
TimelineItem *parent);
void invalidateBar();
int type() const override;
static void updateData(QGraphicsItem *item);
static void invalidateBar(QGraphicsItem *item);
static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b);
void moveAllDurations(qreal offset);
void scaleAllDurations(qreal scale);
qreal firstFrame();
AbstractView *view() const;
bool isSelected() const;
ModelNode targetNode() const;
void updateData();
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void resizeEvent(QGraphicsSceneResizeEvent *event) override;
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
private:
void invalidateHeight();
void invalidateProperties();
bool collapsed() const;
qreal rulerWidth() const;
void toggleCollapsed();
void createPropertyItems();
const QList<QGraphicsItem *> propertyItems() const;
TransitionEditorSectionItem(TimelineItem *parent = nullptr);
ModelNode m_targetNode;
ModelNode m_animationNode;
TransitionEditorBarItem *m_barItem;
TimelineItem *m_dummyItem;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,177 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorsettingsdialog.h"
#include "timelinesettingsdialog.h"
#include "transitioneditorview.h"
#include "ui_transitioneditorsettingsdialog.h"
#include "timelineicons.h"
#include "timelinesettingsmodel.h"
#include "transitionform.h"
#include <abstractview.h>
#include <bindingproperty.h>
#include <exception>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QKeyEvent>
#include <QToolBar>
namespace QmlDesigner {
static void deleteAllTabs(QTabWidget *tabWidget)
{
while (tabWidget->count() > 0) {
QWidget *w = tabWidget->widget(0);
tabWidget->removeTab(0);
delete w;
}
}
static ModelNode getTransitionFromTabWidget(QTabWidget *tabWidget)
{
QWidget *w = tabWidget->currentWidget();
if (w)
return qobject_cast<TransitionForm *>(w)->transition();
return QmlTimeline();
}
static void setTabForTransition(QTabWidget *tabWidget, const ModelNode &timeline)
{
for (int i = 0; i < tabWidget->count(); ++i) {
QWidget *w = tabWidget->widget(i);
if (qobject_cast<TransitionForm *>(w)->transition() == timeline) {
tabWidget->setCurrentIndex(i);
return;
}
}
}
TransitionEditorSettingsDialog::TransitionEditorSettingsDialog(QWidget *parent,
class TransitionEditorView *view)
: QDialog(parent)
, ui(new Ui::TransitionEditorSettingsDialog)
, m_transitionEditorView(view)
{
//m_timelineSettingsModel = new TimelineSettingsModel(this, view);
ui->setupUi(this);
auto *transitionCornerWidget = new QToolBar;
auto *transitionAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(),
tr("Add Transition"));
auto *transitionRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(),
tr("Remove Transition"));
connect(transitionAddAction, &QAction::triggered, this, [this]() {
setupTransitions(m_transitionEditorView->addNewTransition());
});
connect(transitionRemoveAction, &QAction::triggered, this, [this]() {
ModelNode transition = getTransitionFromTabWidget(ui->timelineTab);
if (transition.isValid()) {
transition.destroy();
setupTransitions({});
}
});
transitionCornerWidget->addAction(transitionAddAction);
transitionCornerWidget->addAction(transitionRemoveAction);
ui->timelineTab->setCornerWidget(transitionCornerWidget, Qt::TopRightCorner);
setupTransitions({});
connect(ui->timelineTab, &QTabWidget::currentChanged, this, [this]() {
m_currentTransition = getTransitionFromTabWidget(ui->timelineTab);
});
}
void TransitionEditorSettingsDialog::setCurrentTransition(const ModelNode &timeline)
{
m_currentTransition = timeline;
setTabForTransition(ui->timelineTab, m_currentTransition);
}
TransitionEditorSettingsDialog::~TransitionEditorSettingsDialog()
{
delete ui;
}
void TransitionEditorSettingsDialog::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
/* ignore */
break;
default:
QDialog::keyPressEvent(event);
}
}
void TransitionEditorSettingsDialog::setupTransitions(const ModelNode &newTransition)
{
deleteAllTabs(ui->timelineTab);
const QList<ModelNode> &transitions = m_transitionEditorView->allTransitions();
if (transitions.isEmpty()) {
m_currentTransition = {};
auto transitionForm = new TransitionForm(this);
transitionForm->setDisabled(true);
ui->timelineTab->addTab(transitionForm, tr("No Transition"));
return;
}
for (const auto &transition : transitions)
addTransitionTab(transition);
if (newTransition.isValid()) {
m_currentTransition = newTransition;
} else {
m_currentTransition = transitions.constFirst();
}
setTabForTransition(ui->timelineTab, m_currentTransition);
}
void TransitionEditorSettingsDialog::addTransitionTab(const QmlTimeline &node)
{
auto transitionForm = new TransitionForm(this);
ui->timelineTab->addTab(transitionForm, node.modelNode().displayName());
transitionForm->setTransition(node);
}
} // namespace QmlDesigner

View File

@@ -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 <qmltimeline.h>
#include <QDialog>
QT_FORWARD_DECLARE_CLASS(QSpinBox)
namespace QmlDesigner {
class TransitionForm;
class TransitionEditorView;
namespace Ui {
class TransitionEditorSettingsDialog;
}
class TransitionEditorSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit TransitionEditorSettingsDialog(QWidget *parent, class TransitionEditorView *view);
void setCurrentTransition(const ModelNode &timeline);
~TransitionEditorSettingsDialog() override;
protected:
void keyPressEvent(QKeyEvent *event) override;
private:
void setupTransitions(const ModelNode &node);
void addTransitionTab(const QmlTimeline &node);
Ui::TransitionEditorSettingsDialog *ui;
TransitionEditorView *m_transitionEditorView;
ModelNode m_currentTransition;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::TransitionEditorSettingsDialog</class>
<widget class="QDialog" name="QmlDesigner::TransitionEditorSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>582</height>
</rect>
</property>
<property name="windowTitle">
<string>Transition Settings</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="timelineTab">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QmlDesigner::TransitionEditorSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QmlDesigner::TransitionEditorSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,342 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditortoolbar.h"
#include "transitioneditorgraphicsscene.h"
#include "timelineconstants.h"
#include "timelineicons.h"
#include "timelineview.h"
#include "timelinewidget.h"
#include <designeractionmanager.h>
#include <nodelistproperty.h>
#include <theme.h>
#include <variantproperty.h>
#include <qmlstate.h>
#include <qmltimeline.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
#include <QApplication>
#include <QComboBox>
#include <QIntValidator>
#include <QLineEdit>
#include <QResizeEvent>
#include <QSlider>
#include <cmath>
namespace QmlDesigner {
static bool isSpacer(QObject *object)
{
return object->property("spacer_widget").toBool();
}
static QWidget *createSpacer()
{
QWidget *spacer = new QWidget();
spacer->setProperty("spacer_widget", true);
return spacer;
}
static int controlWidth(QToolBar *bar, QObject *control)
{
QWidget *widget = nullptr;
if (auto *action = qobject_cast<QAction *>(control))
widget = bar->widgetForAction(action);
if (widget == nullptr)
widget = qobject_cast<QWidget *>(control);
if (widget)
return widget->width();
return 0;
}
static QAction *createAction(const Core::Id &id,
const QIcon &icon,
const QString &name,
const QKeySequence &shortcut)
{
QString text = QString("%1 (%2)").arg(name).arg(shortcut.toString());
Core::Context context(TimelineConstants::C_QMLTIMELINE);
auto *action = new QAction(icon, text);
auto *command = Core::ActionManager::registerAction(action, id, context);
command->setDefaultKeySequence(shortcut);
return action;
}
TransitionEditorToolBar::TransitionEditorToolBar(QWidget *parent)
: QToolBar(parent)
, m_grp()
{
setContentsMargins(0, 0, 0, 0);
createLeftControls();
createCenterControls();
createRightControls();
}
void TransitionEditorToolBar::reset() {}
int TransitionEditorToolBar::scaleFactor() const
{
if (m_scale)
return m_scale->value();
return 0;
}
QString TransitionEditorToolBar::currentTransitionId() const
{
return m_transitionComboBox->currentText();
}
void TransitionEditorToolBar::setBlockReflection(bool block)
{
m_blockReflection = block;
}
void TransitionEditorToolBar::updateComboBox(const ModelNode &root)
{
if (root.isValid() && root.hasProperty("transitions")) {
NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions");
if (transitions.isValid())
for (const ModelNode &transition : transitions.directSubNodes())
m_transitionComboBox->addItem(transition.id());
}
}
void TransitionEditorToolBar::setCurrentTransition(const ModelNode &transition)
{
if (m_blockReflection)
return;
if (transition.isValid()) {
m_transitionComboBox->clear();
const ModelNode root = transition.view()->rootModelNode();
updateComboBox(root);
m_transitionComboBox->setCurrentText(transition.id());
} else {
m_transitionComboBox->clear();
m_transitionComboBox->setCurrentText("");
}
}
void TransitionEditorToolBar::setDuration(qreal frame)
{
auto text = QString::number(frame, 'f', 0);
m_duration->setText(text);
}
void TransitionEditorToolBar::setScaleFactor(int factor)
{
const QSignalBlocker blocker(m_scale);
m_scale->setValue(factor);
}
void TransitionEditorToolBar::setActionEnabled(const QString &name, bool enabled)
{
for (auto *action : actions())
if (action->objectName() == name)
action->setEnabled(enabled);
}
void TransitionEditorToolBar::createLeftControls()
{
auto addActionToGroup = [&](QAction *action) {
addAction(action);
m_grp << action;
};
auto addWidgetToGroup = [&](QWidget *widget) {
addWidget(widget);
m_grp << widget;
};
auto addSpacingToGroup = [&](int width) {
auto *widget = new QWidget;
widget->setFixedWidth(width);
addWidget(widget);
m_grp << widget;
};
addSpacingToGroup(5);
auto *settingsAction = createAction(TimelineConstants::C_SETTINGS,
TimelineIcons::ANIMATION.icon(),
tr("Transition Settings"),
QKeySequence(Qt::Key_S));
connect(settingsAction,
&QAction::triggered,
this,
&TransitionEditorToolBar::settingDialogClicked);
addActionToGroup(settingsAction);
addWidgetToGroup(createSpacer());
m_transitionComboBox = new QComboBox(this);
addWidgetToGroup(m_transitionComboBox);
connect(m_transitionComboBox, &QComboBox::currentTextChanged, this, [this]() {
emit currentTransitionChanged(m_transitionComboBox->currentText());
});
}
static QLineEdit *createToolBarLineEdit(QWidget *parent)
{
auto lineEdit = new QLineEdit(parent);
lineEdit->setStyleSheet("* { background-color: rgba(0, 0, 0, 0); }");
lineEdit->setFixedWidth(48);
lineEdit->setAlignment(Qt::AlignCenter);
QPalette pal = parent->palette();
pal.setColor(QPalette::Text, Theme::instance()->color(Utils::Theme::PanelTextColorLight));
lineEdit->setPalette(pal);
QValidator *validator = new QIntValidator(-100000, 100000, lineEdit);
lineEdit->setValidator(validator);
return lineEdit;
}
void TransitionEditorToolBar::createCenterControls()
{
addSpacing(10);
auto *curvePicker = createAction(TimelineConstants::C_CURVE_PICKER,
TimelineIcons::CURVE_EDITOR.icon(),
tr("Easing Curve Editor"),
QKeySequence(Qt::Key_C));
curvePicker->setObjectName("Easing Curve Editor");
connect(curvePicker, &QAction::triggered, this, &TransitionEditorToolBar::openEasingCurveEditor);
addAction(curvePicker);
addSpacing(10);
#if 0
addSeparator();
addSpacing(10);
auto *curveEditor = new QAction(TimelineIcons::CURVE_PICKER.icon(), tr("Curve Editor"));
addAction(curveEditor);
#endif
}
void TransitionEditorToolBar::createRightControls()
{
auto *spacer = createSpacer();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
addWidget(spacer);
addSeparator();
addSpacing(10);
auto *zoomOut = createAction(TimelineConstants::C_ZOOM_OUT,
TimelineIcons::ZOOM_SMALL.icon(),
tr("Zoom Out"),
QKeySequence(QKeySequence::ZoomOut));
connect(zoomOut, &QAction::triggered, [this]() {
m_scale->setValue(m_scale->value() - m_scale->pageStep());
});
addAction(zoomOut);
addSpacing(10);
m_scale = new QSlider(this);
m_scale->setOrientation(Qt::Horizontal);
m_scale->setMaximumWidth(200);
m_scale->setMinimumWidth(100);
m_scale->setMinimum(0);
m_scale->setMaximum(100);
m_scale->setValue(0);
connect(m_scale, &QSlider::valueChanged, this, &TransitionEditorToolBar::scaleFactorChanged);
addWidget(m_scale);
addSpacing(10);
auto *zoomIn = createAction(TimelineConstants::C_ZOOM_IN,
TimelineIcons::ZOOM_BIG.icon(),
tr("Zoom In"),
QKeySequence(QKeySequence::ZoomIn));
connect(zoomIn, &QAction::triggered, [this]() {
m_scale->setValue(m_scale->value() + m_scale->pageStep());
});
addAction(zoomIn);
addSpacing(10);
addSeparator();
m_duration = createToolBarLineEdit(this);
addWidget(m_duration);
auto emitEndChanged = [this]() { emit durationChanged(m_duration->text().toInt()); };
connect(m_duration, &QLineEdit::editingFinished, emitEndChanged);
}
void TransitionEditorToolBar::addSpacing(int width)
{
auto *widget = new QWidget;
widget->setFixedWidth(width);
addWidget(widget);
}
void TransitionEditorToolBar::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event)
int width = 0;
QWidget *spacer = nullptr;
for (auto *object : qAsConst(m_grp)) {
if (isSpacer(object))
spacer = qobject_cast<QWidget *>(object);
else
width += controlWidth(this, object);
}
if (spacer) {
int spacerWidth = TimelineConstants::sectionWidth - width - 12;
spacer->setFixedWidth(spacerWidth > 0 ? spacerWidth : 0);
}
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,92 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "animationcurvedialog.h"
#include "animationcurveeditormodel.h"
#include <QToolBar>
QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QLineEdit)
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_CLASS(QResizeEvent)
QT_FORWARD_DECLARE_CLASS(QSlider)
QT_FORWARD_DECLARE_CLASS(QWidget)
namespace QmlDesigner {
class TimelineWidget;
class QmlTimeline;
class TransitionEditorToolBar : public QToolBar
{
Q_OBJECT
signals:
void settingDialogClicked();
void scaleFactorChanged(int value);
void durationChanged(int value);
void currentTransitionChanged(const QString &name);
void openEasingCurveEditor();
public:
explicit TransitionEditorToolBar(QWidget *parent = nullptr);
void reset();
int scaleFactor() const;
QString currentTransitionId() const;
void setBlockReflection(bool block);
void setCurrentTransition(const ModelNode &transition);
void setDuration(qreal frame);
void setScaleFactor(int factor);
void setActionEnabled(const QString &name, bool enabled);
void updateComboBox(const ModelNode &root);
protected:
void resizeEvent(QResizeEvent *event) override;
private:
void createLeftControls();
void createCenterControls();
void createRightControls();
void addSpacing(int width);
QList<QObject *> m_grp;
QComboBox *m_transitionComboBox = nullptr;
QSlider *m_scale = nullptr;
QLineEdit *m_duration = nullptr;
bool m_blockReflection = false;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,348 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorview.h"
#include "transitioneditortoolbar.h"
#include "transitioneditorwidget.h"
#include "transitioneditorgraphicsscene.h"
#include "transitioneditorsettingsdialog.h"
#include <bindingproperty.h>
#include <exception.h>
#include <modelnodecontextmenu_helper.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <viewmanager.h>
#include <qmldesignericons.h>
#include <qmldesignerplugin.h>
#include <qmlitemnode.h>
#include <qmlobjectnode.h>
#include <qmlstate.h>
#include <qmltimeline.h>
#include <qmltimelinekeyframegroup.h>
#include <designmodecontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QTimer>
namespace QmlDesigner {
TransitionEditorView::TransitionEditorView(QObject *parent)
: AbstractView(parent)
, m_transitionEditorWidget(nullptr)
{
}
TransitionEditorView::~TransitionEditorView() = default;
void TransitionEditorView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
if (m_transitionEditorWidget)
m_transitionEditorWidget->init();
}
void TransitionEditorView::modelAboutToBeDetached(Model *model)
{
m_transitionEditorWidget->reset();
AbstractView::modelAboutToBeDetached(model);
}
void TransitionEditorView::nodeCreated(const ModelNode & /*createdNode*/) {}
void TransitionEditorView::nodeAboutToBeRemoved(const ModelNode & /*removedNode*/) {}
void TransitionEditorView::nodeRemoved(const ModelNode & removedNode,
const NodeAbstractProperty &parentProperty,
PropertyChangeFlags /*propertyChange*/)
{
if (parentProperty.name() == "transitions")
widget()->updateData(removedNode);
}
void TransitionEditorView::nodeReparented(const ModelNode &node,
const NodeAbstractProperty &newPropertyParent,
const NodeAbstractProperty & /*oldPropertyParent*/,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
if (newPropertyParent.name() == "transitions")
asyncUpdate(node);
const ModelNode parent = newPropertyParent.parentModelNode();
qDebug() << Q_FUNC_INFO << parent;
if (parent.isValid() && parent.metaInfo().isValid()
&& parent.metaInfo().isSubclassOf("QtQuick.Transition")) {
asyncUpdate(parent);
}
}
void TransitionEditorView::instancePropertyChanged(
const QList<QPair<ModelNode, PropertyName>> & /*propertyList*/)
{
}
void TransitionEditorView::variantPropertiesChanged(
const QList<VariantProperty> & /* propertyList */,
AbstractView::PropertyChangeFlags /*propertyChange*/)
{
}
void TransitionEditorView::bindingPropertiesChanged(
const QList<BindingProperty> & /*propertyList */,
AbstractView::PropertyChangeFlags /* propertyChange */)
{
}
void TransitionEditorView::selectedNodesChanged(const QList<ModelNode> & /*selectedNodeList*/,
const QList<ModelNode> & /*lastSelectedNodeList*/)
{
}
void TransitionEditorView::propertiesAboutToBeRemoved(
const QList<AbstractProperty> & /*propertyList */)
{
}
void TransitionEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
{
for (const AbstractProperty &property : propertyList) {
if (property.name() == "transitions")
widget()->init();
}
}
bool TransitionEditorView::hasWidget() const
{
return true;
}
void TransitionEditorView::nodeIdChanged(const ModelNode &node, const QString &, const QString &)
{
if (node.metaInfo().isValid() && node.metaInfo().isSubclassOf("QtQuick.Transition"))
widget()->init();
}
void TransitionEditorView::currentStateChanged(const ModelNode &)
{
}
TransitionEditorWidget *TransitionEditorView::widget() const
{
return m_transitionEditorWidget;
}
void TransitionEditorView::registerActions()
{
}
ModelNode TransitionEditorView::addNewTransition()
{
QList<QmlModelState> states;
const ModelNode root = rootModelNode();
if (QmlVisualNode::isValidQmlVisualNode(root)) {
states = QmlVisualNode(root).states().allStates();
}
if (states.isEmpty()) {
Core::AsynchronousMessageBox::warning(tr("No States Defined"),
tr("There are no states defined in this component."));
return {};
}
QHash<QString, QStringList> idPropertyList;
const QVector<TypeName> validProperties = {"int", "real", "double", "qreal", "color", "QColor"};
for (const QmlModelState &state : qAsConst(states)) {
for (const QmlPropertyChanges & change : state.propertyChanges()) {
QStringList locList;
const ModelNode target = change.target();
const QString targetId = target.id();
if (target.isValid() && target.hasMetaInfo()) {
for (const VariantProperty &property : change.modelNode().variantProperties()) {
TypeName typeName = target.metaInfo().propertyTypeName(property.name());
if (validProperties.contains(typeName))
locList.append(QString::fromUtf8(property.name()));
}
if (idPropertyList.contains(targetId)) {
QStringList newlist = idPropertyList.value(targetId);
for (const QString &str :locList)
if (!newlist.contains(str))
newlist.append(str);
idPropertyList.insert(targetId, newlist);
} else {
if (!locList.isEmpty())
idPropertyList.insert(targetId, locList);
}
}
}
}
ModelNode transition;
if (!idPropertyList.isEmpty()) {
executeInTransaction(
" TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() {
transition = createModelNode("QtQuick.Transition",
2,
0,
{{
"from",
"*",
},
{
"to",
"*",
}});
transition.setAuxiliaryData("transitionDuration", 2000);
transition.validId();
root.nodeListProperty("transitions").reparentHere(transition);
for (const QString &id : idPropertyList.keys()) {
ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation",
2,
12);
transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation);
for (const QString &property : idPropertyList.value(id)) {
ModelNode sequentialAnimation
= createModelNode("QtQuick.SequentialAnimation", 2, 12);
parallelAnimation.defaultNodeAbstractProperty().reparentHere(
sequentialAnimation);
ModelNode pauseAnimation = createModelNode("QtQuick.PauseAnimation",
2,
12,
{{"duration", 50}});
sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
pauseAnimation);
ModelNode propertyAnimation = createModelNode("QtQuick.PropertyAnimation",
2,
12,
{{"property", property},
{"duration", 150}});
propertyAnimation.bindingProperty("target").setExpression(id);
sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
propertyAnimation);
}
}
});
}
if (m_transitionEditorWidget)
m_transitionEditorWidget->init();
return transition;
}
TransitionEditorWidget *TransitionEditorView::createWidget()
{
if (!m_transitionEditorWidget)
m_transitionEditorWidget = new TransitionEditorWidget(this);
//auto *timelineContext = new TimelineContext(m_timelineWidget);
//Core::ICore::addContextObject(timelineContext);
return m_transitionEditorWidget;
}
WidgetInfo TransitionEditorView::widgetInfo()
{
return createWidgetInfo(createWidget(),
nullptr,
"TransitionEditor",
WidgetInfo::BottomPane,
0,
tr("Transition Editor"));
}
void TransitionEditorView::openSettingsDialog()
{
auto dialog = new TransitionEditorSettingsDialog(Core::ICore::dialogParent(), this);
auto transition = widget()->graphicsScene()->transitionModelNode();
if (transition.isValid())
dialog->setCurrentTransition(transition);
QObject::connect(dialog, &TransitionEditorSettingsDialog::rejected, [this, dialog]() {
widget()->init();
dialog->deleteLater();
});
QObject::connect(dialog, &TransitionEditorSettingsDialog::accepted, [this, dialog]() {
widget()->init();
dialog->deleteLater();
});
dialog->show();
}
const QList<ModelNode> TransitionEditorView::allTransitions() const
{
if (rootModelNode().isValid() && rootModelNode().hasProperty("transitions")) {
NodeAbstractProperty transitions = rootModelNode().nodeAbstractProperty("transitions");
if (transitions.isValid())
return transitions.directSubNodes();
}
return {};
}
void TransitionEditorView::asyncUpdate(const ModelNode &transition)
{
static bool updateTriggered = false;
if (!updateTriggered && (transition.id() == widget()->toolBar()->currentTransitionId())) {
updateTriggered = true;
QTimer::singleShot(0, [this, transition]() {
widget()->updateData(transition);
updateTriggered = false;
});
}
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "animationcurvedialog.h"
#include "animationcurveeditormodel.h"
#include "treeitem.h"
#include <abstractview.h>
#include <QPointer>
namespace QmlDesigner {
class TransitionEditorWidget;
class TransitionEditorView : public AbstractView
{
Q_OBJECT
public:
explicit TransitionEditorView(QObject *parent = nullptr);
~TransitionEditorView() override;
//Abstract View
WidgetInfo widgetInfo() override;
void modelAttached(Model *model) override;
void modelAboutToBeDetached(Model *model) override;
void nodeCreated(const ModelNode &createdNode) override;
void nodeAboutToBeRemoved(const ModelNode &removedNode) override;
void nodeRemoved(const ModelNode &removedNode,
const NodeAbstractProperty &parentProperty,
PropertyChangeFlags propertyChange) override;
void nodeReparented(const ModelNode &node,
const NodeAbstractProperty &newPropertyParent,
const NodeAbstractProperty &oldPropertyParent,
PropertyChangeFlags propertyChange) override;
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName>> &propertyList) override;
void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
PropertyChangeFlags propertyChange) override;
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
bool hasWidget() const override;
void nodeIdChanged(const ModelNode &node, const QString &, const QString &) override;
void currentStateChanged(const ModelNode &node) override;
TransitionEditorWidget *widget() const;
void insertKeyframe(const ModelNode &target, const PropertyName &propertyName);
void registerActions();
ModelNode addNewTransition();
void openSettingsDialog();
const QList<ModelNode> allTransitions() const;
void asyncUpdate(const ModelNode &transition);
private:
TransitionEditorWidget *createWidget();
TransitionEditorWidget *m_transitionEditorWidget = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,414 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorwidget.h"
#include "transitioneditorgraphicsscene.h"
#include "transitioneditorpropertyitem.h"
#include "transitioneditortoolbar.h"
#include "transitioneditorview.h"
#include <timelineeditor/easingcurvedialog.h>
#include <timelineeditor/timelineconstants.h>
#include <timelineeditor/timelineicons.h>
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <nodemetainfo.h>
#include <qmldesignerplugin.h>
#include <qmlstate.h>
#include <qmltimeline.h>
#include <coreplugin/icore.h>
#include <theme.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <QApplication>
#include <QComboBox>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QLabel>
#include <QMargins>
#include <QPushButton>
#include <QResizeEvent>
#include <QScrollBar>
#include <QShowEvent>
#include <QSlider>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <QtGlobal>
namespace QmlDesigner {
class Eventfilter : public QObject
{
public:
Eventfilter(QObject *parent)
: QObject(parent)
{}
bool eventFilter(QObject *, QEvent *event) override
{
if (event->type() == QEvent::Wheel) {
event->accept();
return true;
}
return false;
}
};
TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view)
: QWidget()
, m_toolbar(new TransitionEditorToolBar(this))
, m_rulerView(new QGraphicsView(this))
, m_graphicsView(new QGraphicsView(this))
, m_scrollbar(new QScrollBar(this))
, m_statusBar(new QLabel(this))
, m_transitionEditorView(view)
, m_graphicsScene(new TransitionEditorGraphicsScene(this))
, m_addButton(new QPushButton(this))
, m_onboardingContainer(new QWidget(this))
{
setWindowTitle(tr("Transition", "Title of transition view"));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
const QString css = Theme::replaceCssColors(QString::fromUtf8(
Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))));
m_scrollbar->setStyleSheet(css);
m_scrollbar->setOrientation(Qt::Horizontal);
QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Preferred);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(m_graphicsView->sizePolicy().hasHeightForWidth());
m_rulerView->setObjectName("RulerView");
m_rulerView->setFixedHeight(TimelineConstants::rulerHeight);
m_rulerView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
m_rulerView->viewport()->installEventFilter(new Eventfilter(this));
m_rulerView->viewport()->setFocusPolicy(Qt::NoFocus);
m_rulerView->setStyleSheet(css);
m_rulerView->setFrameShape(QFrame::NoFrame);
m_rulerView->setFrameShadow(QFrame::Plain);
m_rulerView->setLineWidth(0);
m_rulerView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_rulerView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_rulerView->setScene(graphicsScene());
m_graphicsView->setStyleSheet(css);
m_graphicsView->setObjectName("SceneView");
m_graphicsView->setFrameShape(QFrame::NoFrame);
m_graphicsView->setFrameShadow(QFrame::Plain);
m_graphicsView->setLineWidth(0);
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_graphicsView->setSizePolicy(sizePolicy1);
m_graphicsView->setScene(graphicsScene());
m_graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
m_graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
auto *scrollBarLayout = new QHBoxLayout;
scrollBarLayout->addSpacing(TimelineConstants::sectionWidth);
scrollBarLayout->addWidget(m_scrollbar);
QMargins margins(0, 0, 0, QApplication::style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
auto *contentLayout = new QVBoxLayout;
contentLayout->setContentsMargins(margins);
contentLayout->addWidget(m_rulerView);
contentLayout->addWidget(m_graphicsView);
contentLayout->addLayout(scrollBarLayout);
contentLayout->addWidget(m_statusBar);
m_statusBar->setIndent(2);
m_statusBar->setFixedHeight(TimelineConstants::rulerHeight);
auto *widgetLayout = new QVBoxLayout;
widgetLayout->setContentsMargins(0, 0, 0, 0);
widgetLayout->setSpacing(0);
widgetLayout->addWidget(m_toolbar);
widgetLayout->addWidget(m_addButton);
m_addButton->setIcon(TimelineIcons::ADD_TIMELINE_TOOLBAR.icon());
m_addButton->setToolTip(tr("Add Transition"));
m_addButton->setFlat(true);
m_addButton->setFixedSize(32, 32);
widgetLayout->addWidget(m_onboardingContainer);
auto *onboardingTopLabel = new QLabel(m_onboardingContainer);
auto *onboardingBottomLabel = new QLabel(m_onboardingContainer);
auto *onboardingBottomIcon = new QLabel(m_onboardingContainer);
auto *onboardingLayout = new QVBoxLayout;
auto *onboardingSublayout = new QHBoxLayout;
auto *leftSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
auto *rightSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
auto *topSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
auto *bottomSpacer = new QSpacerItem(40, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
QString labelText = tr("This file does not contain transitions. <br><br> \
To create an animation, add a transition by clicking the + button.");
onboardingTopLabel->setText(labelText);
onboardingTopLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
m_onboardingContainer->setLayout(onboardingLayout);
onboardingLayout->setContentsMargins(0, 0, 0, 0);
onboardingLayout->setSpacing(0);
onboardingLayout->addSpacerItem(topSpacer);
onboardingLayout->addWidget(onboardingTopLabel);
onboardingLayout->addLayout(onboardingSublayout);
onboardingSublayout->setContentsMargins(0, 0, 0, 0);
onboardingSublayout->setSpacing(0);
onboardingSublayout->addSpacerItem(leftSpacer);
onboardingBottomLabel->setAlignment(Qt::AlignRight | Qt::AlignTop);
onboardingBottomLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
onboardingSublayout->addWidget(onboardingBottomLabel);
onboardingBottomLabel->setText(tr("To edit the transition settings, click "));
onboardingBottomIcon->setAlignment(Qt::AlignLeft | Qt::AlignTop);
onboardingBottomIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
onboardingSublayout->addWidget(onboardingBottomIcon);
onboardingBottomIcon->setPixmap(TimelineIcons::ANIMATION.pixmap());
onboardingSublayout->addSpacerItem(rightSpacer);
onboardingLayout->addSpacerItem(bottomSpacer);
widgetLayout->addLayout(contentLayout);
this->setLayout(widgetLayout);
connectToolbar();
auto setScrollOffset = [this]() { graphicsScene()->setScrollOffset(m_scrollbar->value()); };
connect(m_scrollbar, &QSlider::valueChanged, this, setScrollOffset);
connect(graphicsScene(),
&TransitionEditorGraphicsScene::statusBarMessageChanged,
this,
[this](const QString &message) { m_statusBar->setText(message); });
connect(m_addButton, &QPushButton::clicked, this, [this]() {
m_transitionEditorView->addNewTransition();
});
}
void TransitionEditorWidget::setTransitionActive(bool b)
{
if (b) {
m_toolbar->setVisible(true);
m_graphicsView->setVisible(true);
m_rulerView->setVisible(true);
m_scrollbar->setVisible(true);
m_addButton->setVisible(false);
m_onboardingContainer->setVisible(false);
m_graphicsView->update();
m_rulerView->update();
} else {
m_toolbar->setVisible(false);
m_graphicsView->setVisible(false);
m_rulerView->setVisible(false);
m_scrollbar->setVisible(false);
m_addButton->setVisible(true);
m_onboardingContainer->setVisible(true);
}
}
void TransitionEditorWidget::connectToolbar()
{
connect(graphicsScene(),
&TransitionEditorGraphicsScene::selectionChanged,
this,
&TransitionEditorWidget::selectionChanged);
connect(m_toolbar,
&TransitionEditorToolBar::openEasingCurveEditor,
this,
&TransitionEditorWidget::openEasingCurveEditor);
connect(graphicsScene(),
&TransitionEditorGraphicsScene::scroll,
this,
&TransitionEditorWidget::scroll);
auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); };
connect(m_toolbar, &TransitionEditorToolBar::scaleFactorChanged, setRulerScaling);
auto setDuration = [this](int end) { graphicsScene()->setDuration(end); };
connect(m_toolbar, &TransitionEditorToolBar::durationChanged, setDuration);
connect(m_toolbar,
&TransitionEditorToolBar::settingDialogClicked,
transitionEditorView(),
&TransitionEditorView::openSettingsDialog);
connect(m_toolbar,
&TransitionEditorToolBar::currentTransitionChanged,
this,
[this](const QString &transitionName) {
const ModelNode transition = transitionEditorView()->modelNodeForId(transitionName);
if (transition.isValid()) {
m_graphicsScene->setTransition(transition);
}
});
}
void TransitionEditorWidget::changeScaleFactor(int factor)
{
m_toolbar->setScaleFactor(factor);
}
void TransitionEditorWidget::scroll(const TimelineUtils::Side &side)
{
if (side == TimelineUtils::Side::Left)
m_scrollbar->setValue(m_scrollbar->value() - m_scrollbar->singleStep());
else if (side == TimelineUtils::Side::Right)
m_scrollbar->setValue(m_scrollbar->value() + m_scrollbar->singleStep());
}
void TransitionEditorWidget::selectionChanged()
{
if (graphicsScene()->selectedPropertyItem() != nullptr)
m_toolbar->setActionEnabled("Curve Picker", true);
else
m_toolbar->setActionEnabled("Curve Picker", false);
}
void TransitionEditorWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
{
if (transitionEditorView())
transitionEditorView()->contextHelp(callback);
else
callback({});
}
void TransitionEditorWidget::init()
{
ModelNode root = transitionEditorView()->rootModelNode();
ModelNode transition;
if (root.isValid() && root.hasProperty("transitions")) {
NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions");
if (transitions.isValid())
transition = transitions.directSubNodes().first();
}
m_graphicsScene->setTransition(transition);
setTransitionActive(transition.isValid());
m_graphicsScene->setWidth(m_graphicsView->viewport()->width());
m_toolbar->setScaleFactor(0);
m_toolbar->setCurrentTransition(transition);
qreal duration = 2000;
if (transition.isValid() && transition.hasAuxiliaryData("transitionDuration"))
duration = transition.auxiliaryData("transitionDuration").toDouble();
m_toolbar->setDuration(duration);
m_graphicsScene->setRulerScaling(0);
}
void TransitionEditorWidget::updateData(const ModelNode &transition)
{
if (!transition.isValid()) {
init();
return;
}
if (transition.metaInfo().isValid()
&& transition.metaInfo().isSubclassOf("QtQuick.Transition")) {
if (transition.id() == m_toolbar->currentTransitionId()) {
m_graphicsScene->setTransition(transition);
} else {
m_toolbar->updateComboBox(transition.view()->rootModelNode());
}
}
}
void TransitionEditorWidget::reset()
{
graphicsScene()->clearTransition();
m_toolbar->reset();
m_statusBar->clear();
}
TransitionEditorGraphicsScene *TransitionEditorWidget::graphicsScene() const
{
return m_graphicsScene;
}
TransitionEditorToolBar *TransitionEditorWidget::toolBar() const
{
return m_toolbar;
}
void TransitionEditorWidget::setupScrollbar(int min, int max, int current)
{
bool b = m_scrollbar->blockSignals(true);
m_scrollbar->setMinimum(min);
m_scrollbar->setMaximum(max);
m_scrollbar->setValue(current);
m_scrollbar->setSingleStep((max - min) / 10);
m_scrollbar->blockSignals(b);
}
void TransitionEditorWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
graphicsScene()->setWidth(m_graphicsView->viewport()->width());
graphicsScene()->invalidateLayout();
graphicsScene()->invalidate();
graphicsScene()->onShow();
}
void TransitionEditorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
graphicsScene()->setWidth(m_graphicsView->viewport()->width());
}
TransitionEditorView *TransitionEditorWidget::transitionEditorView() const
{
return m_transitionEditorView;
}
void TransitionEditorWidget::openEasingCurveEditor()
{
if (TransitionEditorPropertyItem *item = graphicsScene()->selectedPropertyItem()) {
QList<ModelNode> animations;
animations.append(item->propertyAnimation());
EasingCurveDialog::runDialog(animations, Core::ICore::dialogParent());
}
}
} // namespace QmlDesigner

Some files were not shown because too many files have changed in this diff Show More