forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/4.13'
Change-Id: Ie67085fcbb4150c60f0253312e19a82b95b4a898
This commit is contained in:
@@ -57,11 +57,11 @@
|
||||
To use \QC to develop Qt applications for Android, you need the following:
|
||||
|
||||
\list
|
||||
\li \l{https://www.oracle.com/java/technologies/javase-jdk8-downloads.html}
|
||||
{Java SE Development Kit (JDK)} version 6 up to 8.
|
||||
You can also use \l{http://openjdk.java.net/}{OpenJDK} on Linux.
|
||||
\li \l{AdoptOpenJDK} for all platforms. You can also use \l{OpenJDK}
|
||||
on Linux.
|
||||
|
||||
\note Android SDK Tools have issues with JDK versions later than 8.
|
||||
\note Android SDK Tools versions <= 26.x have issues with JDK versions
|
||||
later than 8. It is recommended to use the latest Command-line SDK Tools.
|
||||
|
||||
\li \l{http://www.gradle.org}{Gradle} for building application packages
|
||||
(APK) and app bundles (AAB) for Android devices. Gradle is delivered
|
||||
|
@@ -59,7 +59,7 @@
|
||||
|
||||
\section1 Adding CMake Tools
|
||||
|
||||
\QC requires CMake's {https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html}
|
||||
\QC requires CMake's \l{https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html}
|
||||
{file-based API}. Please make sure to use CMake version 3.14, or later.
|
||||
|
||||
To specify paths to CMake executables:
|
||||
|
@@ -45,3 +45,11 @@
|
||||
\externalpage https://doc.qt.io/QtForMCUs/qtul-getting-started-windows.html
|
||||
\title Getting Started on Windows
|
||||
*/
|
||||
/*!
|
||||
\externalpage https://adoptopenjdk.net/
|
||||
\title AdoptOpenJDK
|
||||
*/
|
||||
/*!
|
||||
\externalpage http://openjdk.java.net
|
||||
\title OpenJDK
|
||||
*/
|
||||
|
@@ -40,7 +40,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector<InstanceContainer> &instanc
|
||||
const QVector<AddImportContainer> &importVector,
|
||||
const QVector<MockupTypeContainer> &mockupTypeVector,
|
||||
const QUrl &fileUrl,
|
||||
const QHash<QString, QVariantMap> &edit3dToolStates)
|
||||
const QHash<QString, QVariantMap> &edit3dToolStates,
|
||||
const QString &language)
|
||||
: m_instanceVector(instanceContainer),
|
||||
m_reparentInstanceVector(reparentContainer),
|
||||
m_idVector(idVector),
|
||||
@@ -50,7 +51,8 @@ CreateSceneCommand::CreateSceneCommand(const QVector<InstanceContainer> &instanc
|
||||
m_importVector(importVector),
|
||||
m_mockupTypeVector(mockupTypeVector),
|
||||
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;
|
||||
}
|
||||
|
||||
QString CreateSceneCommand::language() const
|
||||
{
|
||||
return m_language;
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command)
|
||||
{
|
||||
out << command.instances();
|
||||
@@ -116,6 +123,7 @@ QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command)
|
||||
out << command.mockupTypes();
|
||||
out << command.fileUrl();
|
||||
out << command.edit3dToolStates();
|
||||
out << command.language();
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -132,6 +140,7 @@ QDataStream &operator>>(QDataStream &in, CreateSceneCommand &command)
|
||||
in >> command.m_mockupTypeVector;
|
||||
in >> command.m_fileUrl;
|
||||
in >> command.m_edit3dToolStates;
|
||||
in >> command.m_language;
|
||||
|
||||
return in;
|
||||
}
|
||||
@@ -148,7 +157,8 @@ QDebug operator <<(QDebug debug, const CreateSceneCommand &command)
|
||||
<< "imports: " << command.imports() << ", "
|
||||
<< "mockupTypes: " << command.mockupTypes() << ", "
|
||||
<< "fileUrl: " << command.fileUrl() << ", "
|
||||
<< "edit3dToolStates: " << command.edit3dToolStates() << ")";
|
||||
<< "edit3dToolStates: " << command.edit3dToolStates() << ", "
|
||||
<< "language: " << command.language() << ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -45,16 +45,18 @@ class CreateSceneCommand
|
||||
|
||||
public:
|
||||
CreateSceneCommand();
|
||||
explicit CreateSceneCommand(const QVector<InstanceContainer> &instanceContainer,
|
||||
const QVector<ReparentContainer> &reparentContainer,
|
||||
const QVector<IdContainer> &idVector,
|
||||
const QVector<PropertyValueContainer> &valueChangeVector,
|
||||
const QVector<PropertyBindingContainer> &bindingChangeVector,
|
||||
const QVector<PropertyValueContainer> &auxiliaryChangeVector,
|
||||
const QVector<AddImportContainer> &importVector,
|
||||
const QVector<MockupTypeContainer> &mockupTypeVector,
|
||||
const QUrl &fileUrl,
|
||||
const QHash<QString, QVariantMap> &edit3dToolStates);
|
||||
explicit CreateSceneCommand(
|
||||
const QVector<InstanceContainer> &instanceContainer,
|
||||
const QVector<ReparentContainer> &reparentContainer,
|
||||
const QVector<IdContainer> &idVector,
|
||||
const QVector<PropertyValueContainer> &valueChangeVector,
|
||||
const QVector<PropertyBindingContainer> &bindingChangeVector,
|
||||
const QVector<PropertyValueContainer> &auxiliaryChangeVector,
|
||||
const QVector<AddImportContainer> &importVector,
|
||||
const QVector<MockupTypeContainer> &mockupTypeVector,
|
||||
const QUrl &fileUrl,
|
||||
const QHash<QString, QVariantMap> &edit3dToolStates,
|
||||
const QString &language);
|
||||
|
||||
QVector<InstanceContainer> instances() const;
|
||||
QVector<ReparentContainer> reparentInstances() const;
|
||||
@@ -66,6 +68,7 @@ public:
|
||||
QVector<MockupTypeContainer> mockupTypes() const;
|
||||
QUrl fileUrl() const;
|
||||
QHash<QString, QVariantMap> edit3dToolStates() const;
|
||||
QString language() const;
|
||||
|
||||
private:
|
||||
QVector<InstanceContainer> m_instanceVector;
|
||||
@@ -78,6 +81,7 @@ private:
|
||||
QVector<MockupTypeContainer> m_mockupTypeVector;
|
||||
QUrl m_fileUrl;
|
||||
QHash<QString, QVariantMap> m_edit3dToolStates;
|
||||
QString m_language;
|
||||
};
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const CreateSceneCommand &command);
|
||||
|
@@ -104,6 +104,7 @@ Model {
|
||||
grabsMouse: targetNode
|
||||
active: rootModel.active
|
||||
dragHelper: rootModel.dragHelper
|
||||
priority: 5
|
||||
|
||||
onPressed: rootModel.handlePressed(mouseAreaYZ, planePos, screenPos)
|
||||
onDragged: rootModel.handleDragged(mouseAreaYZ, planePos, screenPos)
|
||||
@@ -121,6 +122,7 @@ Model {
|
||||
grabsMouse: targetNode
|
||||
active: rootModel.active
|
||||
dragHelper: rootModel.dragHelper
|
||||
priority: 5
|
||||
|
||||
onPressed: rootModel.handlePressed(mouseAreaXZ, planePos, screenPos)
|
||||
onDragged: rootModel.handleDragged(mouseAreaXZ, planePos, screenPos)
|
||||
|
@@ -161,7 +161,7 @@ Node {
|
||||
color: highlightOnHover && (hovering || dragging) ? Qt.lighter(Qt.rgba(0.5, 0.5, 0.5, 1))
|
||||
: Qt.rgba(0.5, 0.5, 0.5, 1)
|
||||
rotation: view3D.camera.rotation
|
||||
priority: 1
|
||||
priority: 10
|
||||
targetNode: moveGizmo.targetNode
|
||||
|
||||
view3D: moveGizmo.view3D
|
||||
|
@@ -185,7 +185,7 @@ Node {
|
||||
height: 120
|
||||
rotation: view3D.camera.rotation
|
||||
grabsMouse: scaleGizmo.targetNode
|
||||
priority: 1
|
||||
priority: 10
|
||||
active: scaleGizmo.visible
|
||||
dragHelper: scaleGizmo.dragHelper
|
||||
|
||||
|
@@ -314,6 +314,7 @@ void NodeInstanceServer::stopRenderTimer()
|
||||
|
||||
void NodeInstanceServer::createScene(const CreateSceneCommand &command)
|
||||
{
|
||||
setTranslationLanguage(command.language());
|
||||
initializeView();
|
||||
|
||||
Internal::QmlPrivateGate::stopUnifiedTimer();
|
||||
@@ -1329,6 +1330,20 @@ void NodeInstanceServer::loadDummyContextObjectFile(const QFileInfo& qmlFileInfo
|
||||
refreshBindings();
|
||||
}
|
||||
|
||||
void NodeInstanceServer::setTranslationLanguage(const QString &language)
|
||||
{
|
||||
static QPointer<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)
|
||||
{
|
||||
QDir dir(directory, "*.qml");
|
||||
@@ -1400,16 +1415,7 @@ void NodeInstanceServer::view3DAction(const View3DActionCommand &command)
|
||||
|
||||
void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command)
|
||||
{
|
||||
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(command.language);
|
||||
}
|
||||
setTranslationLanguage(command.language);
|
||||
QEvent ev(QEvent::LanguageChange);
|
||||
QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);
|
||||
engine()->retranslate();
|
||||
|
@@ -241,6 +241,7 @@ protected:
|
||||
|
||||
virtual void initializeView() = 0;
|
||||
virtual void setupScene(const CreateSceneCommand &command) = 0;
|
||||
void setTranslationLanguage(const QString &language);
|
||||
void loadDummyDataFiles(const QString& directory);
|
||||
void loadDummyDataContext(const QString& directory);
|
||||
void loadDummyDataFile(const QFileInfo& fileInfo);
|
||||
|
@@ -46,6 +46,7 @@ Qt5PreviewNodeInstanceServer::Qt5PreviewNodeInstanceServer(NodeInstanceClientInt
|
||||
|
||||
void Qt5PreviewNodeInstanceServer::createScene(const CreateSceneCommand &command)
|
||||
{
|
||||
setTranslationLanguage(command.language());
|
||||
initializeView();
|
||||
setupScene(command);
|
||||
startRenderTimer();
|
||||
|
@@ -5,9 +5,11 @@ CONFIG += c++11
|
||||
|
||||
DEFINES -= QT_CREATOR
|
||||
|
||||
# This .pri file contains classes to enable a special multilanguage translator debug service
|
||||
# This .pri file contains classes to enable a special multilanguage translator
|
||||
MULTILANGUAGE_SUPPORT_PRI=$$(MULTILANGUAGE_SUPPORT_PRI)
|
||||
!isEmpty(MULTILANGUAGE_SUPPORT_PRI) {
|
||||
exists($$(MULTILANGUAGE_SUPPORT_PRI)): message(including \"$$(MULTILANGUAGE_SUPPORT_PRI)\")
|
||||
else: error("MULTILANGUAGE_SUPPORT_PRI: \"$$(MULTILANGUAGE_SUPPORT_PRI)\" does not exist.")
|
||||
include($$(MULTILANGUAGE_SUPPORT_PRI))
|
||||
DEFINES += MULTILANGUAGE_TRANSLATIONPROVIDER
|
||||
}
|
||||
|
@@ -47,11 +47,11 @@ inline QString qmlDebugServices(QmlDebugServicesPreset preset)
|
||||
case NoQmlDebugServices:
|
||||
return QString();
|
||||
case QmlDebuggerServices:
|
||||
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector");
|
||||
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector,DebugTranslation");
|
||||
case QmlProfilerServices:
|
||||
return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages");
|
||||
return QStringLiteral("CanvasFrameRate,EngineControl,DebugMessages,DebugTranslation");
|
||||
case QmlNativeDebuggerServices:
|
||||
return QStringLiteral("NativeQmlDebugger");
|
||||
return QStringLiteral("NativeQmlDebugger,DebugTranslation");
|
||||
case QmlPreviewServices:
|
||||
return QStringLiteral("QmlPreview,DebugTranslation");
|
||||
default:
|
||||
|
@@ -621,7 +621,6 @@ class UnsupportedTypesByQmlUi : public QStringList
|
||||
public:
|
||||
UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect",
|
||||
"Component",
|
||||
"Transition",
|
||||
"Drawer"})
|
||||
{
|
||||
append(UnsupportedTypesByVisualDesigner());
|
||||
|
@@ -251,10 +251,46 @@ sqlite3 *BaseStatement::sqliteDatabaseHandle() const
|
||||
void BaseStatement::checkForStepError(int resultCode) const
|
||||
{
|
||||
switch (resultCode) {
|
||||
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!");
|
||||
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!");
|
||||
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||
case SQLITE_BUSY:
|
||||
throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to "
|
||||
"acquire the database locks!");
|
||||
case SQLITE_ERROR:
|
||||
throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a "
|
||||
"constraint violation) has occurred!");
|
||||
case SQLITE_MISUSE:
|
||||
throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||
case SQLITE_CONSTRAINT:
|
||||
throwConstraintPreventsModification(
|
||||
"SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||
case SQLITE_TOOBIG:
|
||||
throwTooBig("SqliteStatement::stepStatement: Some is to bigger than SQLITE_MAX_LENGTH.");
|
||||
case SQLITE_SCHEMA:
|
||||
throwSchemaChangeError("SqliteStatement::stepStatement: Schema changed but the statement "
|
||||
"cannot be recompiled.");
|
||||
case SQLITE_READONLY:
|
||||
throwCannotWriteToReadOnlyConnection(
|
||||
"SqliteStatement::stepStatement: Cannot write to read only connection");
|
||||
case SQLITE_PROTOCOL:
|
||||
throwProtocolError(
|
||||
"SqliteStatement::stepStatement: Something strang with the file locking happened.");
|
||||
case SQLITE_NOMEM:
|
||||
throw std::bad_alloc();
|
||||
case SQLITE_NOLFS:
|
||||
throwDatabaseExceedsMaximumFileSize(
|
||||
"SqliteStatement::stepStatement: Database exceeds maximum file size.");
|
||||
case SQLITE_MISMATCH:
|
||||
throwDataTypeMismatch(
|
||||
"SqliteStatement::stepStatement: Most probably you used not an integer for a rowid.");
|
||||
case SQLITE_LOCKED:
|
||||
throwConnectionIsLocked("SqliteStatement::stepStatement: Database connection is locked.");
|
||||
case SQLITE_IOERR:
|
||||
throwInputOutputError("SqliteStatement::stepStatement: An IO error happened.");
|
||||
case SQLITE_INTERRUPT:
|
||||
throwExecutionInterrupted("SqliteStatement::stepStatement: Execution was interrupted.");
|
||||
case SQLITE_CORRUPT:
|
||||
throwDatabaseIsCorrupt("SqliteStatement::stepStatement: Database is corrupt.");
|
||||
case SQLITE_CANTOPEN:
|
||||
throwCannotOpen("SqliteStatement::stepStatement: Cannot open database or temporary file.");
|
||||
}
|
||||
|
||||
throwUnknowError("SqliteStatement::stepStatement: unknown error has happened");
|
||||
@@ -263,10 +299,17 @@ void BaseStatement::checkForStepError(int resultCode) const
|
||||
void BaseStatement::checkForResetError(int resultCode) const
|
||||
{
|
||||
switch (resultCode) {
|
||||
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to acquire the database locks!");
|
||||
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a constraint violation) has occurred!");
|
||||
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||
case SQLITE_CONSTRAINT: throwConstraintPreventsModification("SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||
case SQLITE_BUSY:
|
||||
throwStatementIsBusy("SqliteStatement::stepStatement: database engine was unable to "
|
||||
"acquire the database locks!");
|
||||
case SQLITE_ERROR:
|
||||
throwStatementHasError("SqliteStatement::stepStatement: run-time error (such as a "
|
||||
"constraint violation) has occurred!");
|
||||
case SQLITE_MISUSE:
|
||||
throwStatementIsMisused("SqliteStatement::stepStatement: was called inappropriately!");
|
||||
case SQLITE_CONSTRAINT:
|
||||
throwConstraintPreventsModification(
|
||||
"SqliteStatement::stepStatement: contraint prevent insert or update!");
|
||||
}
|
||||
|
||||
throwUnknowError("SqliteStatement::reset: unknown error has happened");
|
||||
@@ -278,7 +321,8 @@ void BaseStatement::checkForPrepareError(int resultCode) const
|
||||
case SQLITE_BUSY: throwStatementIsBusy("SqliteStatement::prepareStatement: database engine was unable to acquire the database locks!");
|
||||
case SQLITE_ERROR : throwStatementHasError("SqliteStatement::prepareStatement: run-time error (such as a constraint violation) has occurred!");
|
||||
case SQLITE_MISUSE: throwStatementIsMisused("SqliteStatement::prepareStatement: was called inappropriately!");
|
||||
case SQLITE_IOERR: throwIoError("SqliteStatement::prepareStatement: IO error happened!");
|
||||
case SQLITE_IOERR:
|
||||
throwInputOutputError("SqliteStatement::prepareStatement: IO error happened!");
|
||||
}
|
||||
|
||||
throwUnknowError("SqliteStatement::prepareStatement: unknown error has happened");
|
||||
@@ -346,9 +390,9 @@ void BaseStatement::throwStatementIsMisused(const char *whatHasHappened) const
|
||||
throw StatementIsMisused(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||
}
|
||||
|
||||
void BaseStatement::throwIoError(const char *whatHasHappened) const
|
||||
void BaseStatement::throwInputOutputError(const char *whatHasHappened) const
|
||||
{
|
||||
throw IoError(whatHasHappened);
|
||||
throw InputOutputError(whatHasHappened);
|
||||
}
|
||||
|
||||
void BaseStatement::throwConstraintPreventsModification(const char *whatHasHappened) const
|
||||
@@ -384,6 +428,56 @@ void BaseStatement::throwBingingTooBig(const char *whatHasHappened) const
|
||||
throw BindingTooBig(whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle()));
|
||||
}
|
||||
|
||||
void BaseStatement::throwTooBig(const char *whatHasHappened) const
|
||||
{
|
||||
throw TooBig{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwSchemaChangeError(const char *whatHasHappened) const
|
||||
{
|
||||
throw SchemeChangeError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const
|
||||
{
|
||||
throw CannotWriteToReadOnlyConnection{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwProtocolError(const char *whatHasHappened) const
|
||||
{
|
||||
throw ProtocolError{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const
|
||||
{
|
||||
throw DatabaseExceedsMaximumFileSize{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwDataTypeMismatch(const char *whatHasHappened) const
|
||||
{
|
||||
throw DataTypeMismatch{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwConnectionIsLocked(const char *whatHasHappened) const
|
||||
{
|
||||
throw ConnectionIsLocked{whatHasHappened, sqlite3_errmsg(sqliteDatabaseHandle())};
|
||||
}
|
||||
|
||||
void BaseStatement::throwExecutionInterrupted(const char *whatHasHappened) const
|
||||
{
|
||||
throw ExecutionInterrupted{whatHasHappened};
|
||||
}
|
||||
|
||||
void BaseStatement::throwDatabaseIsCorrupt(const char *whatHasHappened) const
|
||||
{
|
||||
throw DatabaseIsCorrupt{whatHasHappened};
|
||||
}
|
||||
|
||||
void BaseStatement::throwCannotOpen(const char *whatHasHappened) const
|
||||
{
|
||||
throw CannotOpen{whatHasHappened};
|
||||
}
|
||||
|
||||
QString BaseStatement::columnName(int column) const
|
||||
{
|
||||
return QString::fromUtf8(sqlite3_column_name(m_compiledStatement.get(), column));
|
||||
|
@@ -109,7 +109,7 @@ public:
|
||||
[[noreturn]] void throwStatementIsBusy(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwStatementHasError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwStatementIsMisused(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwIoError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwInputOutputError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwConstraintPreventsModification(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwNoValuesToFetch(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwInvalidColumnFetched(const char *whatHasHappened) const;
|
||||
@@ -117,6 +117,16 @@ public:
|
||||
[[noreturn]] void throwWrongBingingName(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwUnknowError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwBingingTooBig(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwTooBig(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwSchemaChangeError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwCannotWriteToReadOnlyConnection(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwProtocolError(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwDatabaseExceedsMaximumFileSize(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwDataTypeMismatch(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwConnectionIsLocked(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwExecutionInterrupted(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwDatabaseIsCorrupt(const char *whatHasHappened) const;
|
||||
[[noreturn]] void throwCannotOpen(const char *whatHasHappened) const;
|
||||
|
||||
QString columnName(int column) const;
|
||||
|
||||
|
@@ -91,10 +91,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IoError : public Exception
|
||||
class InputOutputError : public Exception
|
||||
{
|
||||
public:
|
||||
IoError(const char *whatErrorHasHappen)
|
||||
InputOutputError(const char *whatErrorHasHappen)
|
||||
: Exception(whatErrorHasHappen)
|
||||
{
|
||||
}
|
||||
@@ -267,6 +267,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TooBig : public Exception
|
||||
{
|
||||
public:
|
||||
TooBig(const char *whatErrorHasHappen, Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class CannotConvert : public Exception
|
||||
{
|
||||
public:
|
||||
@@ -299,4 +307,84 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class SchemeChangeError : public Exception
|
||||
{
|
||||
public:
|
||||
SchemeChangeError(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class CannotWriteToReadOnlyConnection : public Exception
|
||||
{
|
||||
public:
|
||||
CannotWriteToReadOnlyConnection(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class ProtocolError : public Exception
|
||||
{
|
||||
public:
|
||||
ProtocolError(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class DatabaseExceedsMaximumFileSize : public Exception
|
||||
{
|
||||
public:
|
||||
DatabaseExceedsMaximumFileSize(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class DataTypeMismatch : public Exception
|
||||
{
|
||||
public:
|
||||
DataTypeMismatch(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class ConnectionIsLocked : public Exception
|
||||
{
|
||||
public:
|
||||
ConnectionIsLocked(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class ExecutionInterrupted : public Exception
|
||||
{
|
||||
public:
|
||||
ExecutionInterrupted(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class DatabaseIsCorrupt : public Exception
|
||||
{
|
||||
public:
|
||||
DatabaseIsCorrupt(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
|
||||
class CannotOpen : public Exception
|
||||
{
|
||||
public:
|
||||
CannotOpen(const char *whatErrorHasHappen,
|
||||
Utils::SmallString &&errorMessage = Utils::SmallString())
|
||||
: Exception(whatErrorHasHappen, std::move(errorMessage))
|
||||
{}
|
||||
};
|
||||
} // namespace Sqlite
|
||||
|
@@ -454,8 +454,6 @@ AndroidSettingsWidget::AndroidSettingsWidget()
|
||||
m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
m_ui.AVDTableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
|
||||
m_ui.downloadOpenJDKToolButton->setVisible(!HostOsInfo::isLinuxHost());
|
||||
|
||||
const QIcon downloadIcon = Icons::ONLINE.icon();
|
||||
m_ui.downloadSDKToolButton->setIcon(downloadIcon);
|
||||
m_ui.downloadNDKToolButton->setIcon(downloadIcon);
|
||||
@@ -696,7 +694,10 @@ void AndroidSettingsWidget::openNDKDownloadUrl()
|
||||
|
||||
void AndroidSettingsWidget::openOpenJDKDownloadUrl()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl::fromUserInput("https://www.oracle.com/java/technologies/javase-jdk8-downloads.html"));
|
||||
if (HostOsInfo::isLinuxHost())
|
||||
QDesktopServices::openUrl(QUrl::fromUserInput("https://openjdk.java.net/install/"));
|
||||
else
|
||||
QDesktopServices::openUrl(QUrl::fromUserInput("https://adoptopenjdk.net/"));
|
||||
}
|
||||
|
||||
void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent)
|
||||
|
@@ -153,12 +153,15 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
|
||||
}
|
||||
return true;
|
||||
#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
|
||||
#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 "
|
||||
"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;
|
||||
#endif
|
||||
}
|
||||
|
@@ -38,6 +38,29 @@ if (APPLE)
|
||||
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
|
||||
endif()
|
||||
|
||||
|
||||
add_qtc_plugin(assetexporterplugin
|
||||
CONDITION TARGET QmlDesigner
|
||||
DEPENDS Core ProjectExplorer QmlDesigner Utils Qt5::Qml
|
||||
PUBLIC_INCLUDES assetexporterplugin
|
||||
SOURCES
|
||||
assetexporterplugin/assetexportdialog.h assetexporterplugin/assetexportdialog.cpp assetexporterplugin/assetexportdialog.ui
|
||||
assetexporterplugin/assetexporter.h assetexporterplugin/assetexporter.cpp
|
||||
assetexporterplugin/assetexporterplugin.h assetexporterplugin/assetexporterplugin.cpp
|
||||
assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp
|
||||
assetexporterplugin/assetexportpluginconstants.h
|
||||
assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp
|
||||
assetexporterplugin/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
|
||||
CONDITION TARGET QmlDesigner
|
||||
DEPENDS Core QmlDesigner Utils Qt5::Qml
|
||||
@@ -646,6 +669,22 @@ extend_qtc_plugin(QmlDesigner
|
||||
timelinewidget.cpp timelinewidget.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
SOURCES_PREFIX components/transitioneditor
|
||||
SOURCES
|
||||
transitioneditorview.cpp transitioneditorview.h
|
||||
transitioneditorwidget.cpp transitioneditorwidget.h
|
||||
transitioneditortoolbar.cpp transitioneditortoolbar.h
|
||||
transitioneditorgraphicsscene.cpp transitioneditorgraphicsscene.h
|
||||
transitioneditorgraphicslayout.cpp transitioneditorgraphicslayout.h
|
||||
transitioneditorsectionitem.cpp transitioneditorsectionitem.h
|
||||
transitioneditorpropertyitem.cpp transitioneditorpropertyitem.h
|
||||
transitioneditorsettingsdialog.cpp transitioneditorsettingsdialog.h
|
||||
transitioneditorsettingsdialog.ui
|
||||
transitionform.cpp transitionform.h
|
||||
transitioneditor.qrc
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
SOURCES_PREFIX components/curveeditor
|
||||
SOURCES
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
};
|
||||
|
||||
}
|
@@ -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>
|
370
src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
Normal file
370
src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
Normal 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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
111
src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
Normal file
111
src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
Normal 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
|
@@ -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
|
@@ -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
|
@@ -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"
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
MetaInfo {
|
||||
}
|
@@ -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
|
@@ -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
|
||||
|
@@ -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"
|
||||
]
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/assetexporterplugin">
|
||||
<file>assetexporterplugin.metainfo</file>
|
||||
</qresource>
|
||||
</RCC>
|
@@ -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;
|
||||
}
|
@@ -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);
|
@@ -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";
|
||||
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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
|
@@ -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
|
163
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
Normal file
163
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
60
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
Normal file
60
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
};
|
||||
|
||||
}
|
@@ -83,6 +83,8 @@ const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedC
|
||||
const char flowAssignEffectCommandId[] = "AssignFlowEffect";
|
||||
const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect";
|
||||
const char addToGroupItemCommandId[] = "AddToGroupItem";
|
||||
const char fitRootToScreenCommandId[] = "FitRootToScreen";
|
||||
const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
|
||||
|
||||
const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection");
|
||||
const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect");
|
||||
@@ -155,6 +157,9 @@ const char layoutFillHeightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContext
|
||||
const char flowAssignEffectDisplayName[] = "Assign FlowEffect ";
|
||||
const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect ";
|
||||
|
||||
const char fitRootToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit root to screen");
|
||||
const char fitSelectionToScreenDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit selection to screen");
|
||||
|
||||
const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected item.");
|
||||
const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected item.");
|
||||
|
||||
@@ -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 addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action.");
|
||||
|
||||
const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space.");
|
||||
const char fitSelectionToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the selected elements inside the available space.");
|
||||
|
||||
const int priorityFirst = 280;
|
||||
const int prioritySelectionCategory = 220;
|
||||
const int priorityQmlPreviewCategory = 200;
|
||||
|
@@ -821,6 +821,27 @@ void DesignerActionManager::createDefaultDesignerActions()
|
||||
&resetSize,
|
||||
&selectionNotEmptyAndHasWidthOrHeightProperty));
|
||||
|
||||
addDesignerAction(new ModelNodeAction(
|
||||
fitRootToScreenCommandId,
|
||||
fitRootToScreenDisplayName,
|
||||
Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(),
|
||||
fitRootToScreenToolTip,
|
||||
genericToolBarCategory,
|
||||
QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_0),
|
||||
182,
|
||||
&fitRootToScreen));
|
||||
|
||||
addDesignerAction(new ModelNodeAction(
|
||||
fitSelectionToScreenCommandId,
|
||||
fitSelectionToScreenDisplayName,
|
||||
Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}}).icon(),
|
||||
fitSelectionToScreenToolTip,
|
||||
genericToolBarCategory,
|
||||
QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_I),
|
||||
183,
|
||||
&fitSelectionToScreen,
|
||||
&selectionNotEmpty));
|
||||
|
||||
addDesignerAction(new SeperatorDesignerAction(editCategory, 170));
|
||||
|
||||
addDesignerAction(new VisiblityModelNodeAction(
|
||||
|
@@ -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)
|
||||
{
|
||||
goIntoComponent(selectionState.currentSingleSelectedNode());
|
||||
|
@@ -49,6 +49,8 @@ void setFillWidth(const SelectionContext &selectionState);
|
||||
void setFillHeight(const SelectionContext &selectionState);
|
||||
void resetSize(const SelectionContext &selectionState);
|
||||
void resetPosition(const SelectionContext &selectionState);
|
||||
void fitRootToScreen(const SelectionContext &selectionState);
|
||||
void fitSelectionToScreen(const SelectionContext &selectionState);
|
||||
void goIntoComponentOperation(const SelectionContext &selectionState);
|
||||
void setId(const SelectionContext &selectionState);
|
||||
void resetZ(const SelectionContext &selectionState);
|
||||
|
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
const int defaultZoomIndex = 11;
|
||||
const int defaultZoomIndex = 13;
|
||||
|
||||
ZoomAction::ZoomAction(QObject *parent)
|
||||
: QWidgetAction(parent),
|
||||
@@ -69,14 +69,19 @@ void ZoomAction::setZoomLevel(float zoomLevel)
|
||||
if (qFuzzyCompare(m_zoomLevel, zoomLevel))
|
||||
return;
|
||||
|
||||
forceZoomLevel(zoomLevel);
|
||||
}
|
||||
|
||||
void ZoomAction::forceZoomLevel(float zoomLevel)
|
||||
{
|
||||
m_zoomLevel = qBound(0.01f, zoomLevel, 16.0f);
|
||||
emit zoomLevelChanged(m_zoomLevel);
|
||||
}
|
||||
|
||||
//initial m_zoomLevel and m_currentComboBoxIndex
|
||||
const QVector<float> s_zoomFactors = {0.01f, 0.02f, 0.05f, 0.0625f, 0.125f, 0.25f,
|
||||
0.33f, 0.5f, 0.66f, 0.75f, 0.9f, 1.0f, 1.25f,
|
||||
1.5f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f };
|
||||
const QVector<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.1f, 1.25f, 1.33f,
|
||||
1.5f, 1.66f, 1.75f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 16.0f };
|
||||
|
||||
int getZoomIndex(float zoom)
|
||||
{
|
||||
@@ -87,6 +92,15 @@ int getZoomIndex(float zoom)
|
||||
return -1;
|
||||
}
|
||||
|
||||
float ZoomAction::getClosestZoomLevel(float zoomLevel)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < s_zoomFactors.size() && s_zoomFactors[i] < zoomLevel)
|
||||
++i;
|
||||
|
||||
return s_zoomFactors[qBound(0, i - 1, s_zoomFactors.size() - 1)];
|
||||
}
|
||||
|
||||
QWidget *ZoomAction::createWidget(QWidget *parent)
|
||||
{
|
||||
auto comboBox = new QComboBox(parent);
|
||||
|
@@ -48,6 +48,9 @@ public:
|
||||
void zoomOut();
|
||||
void resetZoomLevel();
|
||||
void setZoomLevel(float zoomLevel);
|
||||
void forceZoomLevel(float zoomLevel);
|
||||
|
||||
static float getClosestZoomLevel(float zoomLevel);
|
||||
|
||||
protected:
|
||||
QWidget *createWidget(QWidget *parent) override;
|
||||
|
@@ -269,6 +269,9 @@ void ConnectionModel::addConnection()
|
||||
if (QmlItemNode(selectedNode).isFlowActionArea())
|
||||
source = selectedNode.validId() + ".trigger()";
|
||||
|
||||
if (QmlVisualNode(selectedNode).isFlowTransition())
|
||||
source = selectedNode.validId() + ".trigger()";
|
||||
|
||||
if (!connectionView()->selectedModelNodes().constFirst().id().isEmpty())
|
||||
newNode.bindingProperty("target").setExpression(selectedNode.id());
|
||||
else
|
||||
|
@@ -112,7 +112,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
|
||||
if (sceneState.contains(sceneKey)) {
|
||||
qint32 newActiveScene = sceneState[sceneKey].value<qint32>();
|
||||
edit3DWidget()->canvas()->updateActiveScene(newActiveScene);
|
||||
rootModelNode().setAuxiliaryData("3d-active-scene", newActiveScene);
|
||||
rootModelNode().setAuxiliaryData("active3dScene", newActiveScene);
|
||||
}
|
||||
|
||||
if (sceneState.contains(selectKey))
|
||||
|
@@ -129,7 +129,7 @@ void FormEditorItem::setup()
|
||||
|
||||
QRectF FormEditorItem::boundingRect() const
|
||||
{
|
||||
return m_boundingRect.adjusted(-2, -2, 2, 2);
|
||||
return m_boundingRect;
|
||||
}
|
||||
|
||||
QPainterPath FormEditorItem::shape() const
|
||||
@@ -150,7 +150,7 @@ void FormEditorItem::updateGeometry()
|
||||
prepareGeometryChange();
|
||||
m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.);
|
||||
m_paintedBoundingRect = qmlItemNode().instancePaintedBoundingRect();
|
||||
m_boundingRect = m_paintedBoundingRect.united(m_selectionBoundingRect);
|
||||
m_boundingRect = qmlItemNode().instanceBoundingRect();
|
||||
setTransform(qmlItemNode().instanceTransformWithContentTransform());
|
||||
// the property for zValue is called z in QGraphicsObject
|
||||
if (qmlItemNode().instanceValue("z").isValid() && !qmlItemNode().isRootModelNode())
|
||||
@@ -739,7 +739,7 @@ static bool isValid(const QList<QmlItemNode> &list)
|
||||
if (!item.isValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return !list.isEmpty();
|
||||
}
|
||||
|
||||
static bool isModelNodeValid(const QList<QmlItemNode> &list)
|
||||
@@ -748,7 +748,7 @@ static bool isModelNodeValid(const QList<QmlItemNode> &list)
|
||||
if (!item.modelNode().isValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return !list.isEmpty();
|
||||
}
|
||||
|
||||
class ResolveConnection
|
||||
@@ -797,17 +797,17 @@ public:
|
||||
if (f.isValid()) {
|
||||
for (const QmlFlowActionAreaNode &area : f.flowActionAreas()) {
|
||||
ModelNode target = area.targetTransition();
|
||||
if (target == node.modelNode()) {
|
||||
if (target == node.modelNode())
|
||||
areaNode = area;
|
||||
} else {
|
||||
const ModelNode decisionNode = area.decisionNodeForTransition(node.modelNode());
|
||||
if (decisionNode.isValid()) {
|
||||
from.clear();
|
||||
from.append(decisionNode);
|
||||
areaNode = ModelNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ModelNode decisionNode = QmlFlowItemNode::decisionNodeForTransition(node.modelNode());
|
||||
if (decisionNode.isValid()) {
|
||||
from.clear();
|
||||
from.append(decisionNode);
|
||||
areaNode = ModelNode();
|
||||
}
|
||||
|
||||
if (f.modelNode().hasAuxiliaryData("joinConnection"))
|
||||
joinConnection = f.modelNode().auxiliaryData("joinConnection").toBool();
|
||||
} else {
|
||||
|
@@ -442,12 +442,49 @@ void FormEditorView::documentMessagesChanged(const QList<DocumentMessage> &error
|
||||
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"))
|
||||
m_dragTool->clearMoveDelay();
|
||||
if (identifier == QLatin1String("reset QmlPuppet"))
|
||||
temporaryBlockView();
|
||||
if (identifier == QLatin1String("fit root to screen")) {
|
||||
if (QmlItemNode(rootModelNode()).isFlowView()) {
|
||||
QRectF boundingRect;
|
||||
for (QGraphicsItem *item : scene()->items()) {
|
||||
if (auto formEditorItem = FormEditorItem::fromQGraphicsItem(item)) {
|
||||
if (!formEditorItem->qmlItemNode().modelNode().isRootNode()
|
||||
&& !formEditorItem->sceneBoundingRect().isNull())
|
||||
boundingRect = boundingRect.united(formEditorItem->sceneBoundingRect());
|
||||
}
|
||||
}
|
||||
m_formEditorWidget->graphicsView()->fitInView(boundingRect,
|
||||
Qt::KeepAspectRatio);
|
||||
} else {
|
||||
m_formEditorWidget->graphicsView()->fitInView(m_formEditorWidget->rootItemRect(),
|
||||
Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
const qreal scaleFactor = m_formEditorWidget->graphicsView()->viewportTransform().m11();
|
||||
float zoomLevel = ZoomAction::getClosestZoomLevel(scaleFactor);
|
||||
m_formEditorWidget->zoomAction()->forceZoomLevel(zoomLevel);
|
||||
}
|
||||
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
|
||||
|
@@ -469,7 +469,7 @@ void DesignDocument::paste()
|
||||
[](const ModelNode &node) { return !node.isSubclassOf("QtQuick3D.Node"); })
|
||||
== selectedNodes.end();
|
||||
if (all3DNodes) {
|
||||
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt();
|
||||
int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt();
|
||||
if (activeSceneId != -1) {
|
||||
NodeListProperty sceneNodeProperty
|
||||
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);
|
||||
@@ -515,7 +515,7 @@ void DesignDocument::paste()
|
||||
} else {
|
||||
// if selection is empty and this is a 3D Node, paste it under the active scene
|
||||
if (pastedNode.isSubclassOf("QtQuick3D.Node")) {
|
||||
int activeSceneId = rootModelNode().auxiliaryData("3d-active-scene").toInt();
|
||||
int activeSceneId = rootModelNode().auxiliaryData("active3dScene").toInt();
|
||||
if (activeSceneId != -1) {
|
||||
NodeListProperty sceneNodeProperty
|
||||
= QmlVisualNode::findSceneNodeProperty(rootModelNode().view(), activeSceneId);
|
||||
|
@@ -469,8 +469,8 @@ void PropertyEditorView::setupQmlBackend()
|
||||
if (m_selectedNode.isValid()) {
|
||||
qmlObjectNode = QmlObjectNode(m_selectedNode);
|
||||
Q_ASSERT(qmlObjectNode.isValid());
|
||||
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
}
|
||||
currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this);
|
||||
currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false));
|
||||
if (specificQmlData.isEmpty())
|
||||
currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData);
|
||||
|
@@ -31,12 +31,12 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene)
|
||||
TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene)
|
||||
: m_scene(scene)
|
||||
, m_delegate(nullptr)
|
||||
{}
|
||||
|
||||
TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene,
|
||||
TimelineAbstractTool::TimelineAbstractTool(AbstractScrollGraphicsScene *scene,
|
||||
TimelineToolDelegate *delegate)
|
||||
: m_scene(scene)
|
||||
, m_delegate(delegate)
|
||||
@@ -44,7 +44,7 @@ TimelineAbstractTool::TimelineAbstractTool(TimelineGraphicsScene *scene,
|
||||
|
||||
TimelineAbstractTool::~TimelineAbstractTool() = default;
|
||||
|
||||
TimelineGraphicsScene *TimelineAbstractTool::scene() const
|
||||
AbstractScrollGraphicsScene *TimelineAbstractTool::scene() const
|
||||
{
|
||||
return m_scene;
|
||||
}
|
||||
|
@@ -36,14 +36,14 @@ namespace QmlDesigner {
|
||||
enum class ToolType { Move, Select };
|
||||
|
||||
class TimelineMovableAbstractItem;
|
||||
class TimelineGraphicsScene;
|
||||
class AbstractScrollGraphicsScene;
|
||||
class TimelineToolDelegate;
|
||||
|
||||
class TimelineAbstractTool
|
||||
{
|
||||
public:
|
||||
explicit TimelineAbstractTool(TimelineGraphicsScene *scene);
|
||||
explicit TimelineAbstractTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene);
|
||||
explicit TimelineAbstractTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
virtual ~TimelineAbstractTool();
|
||||
|
||||
virtual void mousePressEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) = 0;
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
virtual void keyPressEvent(QKeyEvent *keyEvent) = 0;
|
||||
virtual void keyReleaseEvent(QKeyEvent *keyEvent) = 0;
|
||||
|
||||
TimelineGraphicsScene *scene() const;
|
||||
AbstractScrollGraphicsScene *scene() const;
|
||||
|
||||
TimelineToolDelegate *delegate() const;
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
TimelineMovableAbstractItem *currentItem() const;
|
||||
|
||||
private:
|
||||
TimelineGraphicsScene *m_scene;
|
||||
AbstractScrollGraphicsScene *m_scene;
|
||||
|
||||
TimelineToolDelegate *m_delegate;
|
||||
};
|
||||
|
@@ -91,7 +91,7 @@ QList<QmlTimelineKeyframeGroup> allTimelineFrames(const QmlTimeline &timeline)
|
||||
}
|
||||
|
||||
TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *parent)
|
||||
: QGraphicsScene(parent)
|
||||
: AbstractScrollGraphicsScene(parent)
|
||||
, m_parent(parent)
|
||||
, m_layout(new TimelineGraphicsLayout(this))
|
||||
, m_currentFrameIndicator(new TimelineFrameHandle)
|
||||
@@ -378,17 +378,17 @@ void TimelineGraphicsScene::commitCurrentFrame(qreal frame)
|
||||
}
|
||||
}
|
||||
|
||||
QList<TimelineKeyframeItem *> TimelineGraphicsScene::selectedKeyframes() const
|
||||
QList<TimelineKeyframeItem *> AbstractScrollGraphicsScene::selectedKeyframes() const
|
||||
{
|
||||
return m_selectedKeyframes;
|
||||
}
|
||||
|
||||
bool TimelineGraphicsScene::hasSelection() const
|
||||
bool AbstractScrollGraphicsScene::hasSelection() const
|
||||
{
|
||||
return !m_selectedKeyframes.empty();
|
||||
}
|
||||
|
||||
bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const
|
||||
bool AbstractScrollGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const
|
||||
{
|
||||
if (m_selectedKeyframes.empty())
|
||||
return false;
|
||||
@@ -396,12 +396,12 @@ bool TimelineGraphicsScene::isCurrent(TimelineKeyframeItem *keyframe) const
|
||||
return m_selectedKeyframes.back() == keyframe;
|
||||
}
|
||||
|
||||
bool TimelineGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const
|
||||
bool AbstractScrollGraphicsScene::isKeyframeSelected(TimelineKeyframeItem *keyframe) const
|
||||
{
|
||||
return m_selectedKeyframes.contains(keyframe);
|
||||
}
|
||||
|
||||
bool TimelineGraphicsScene::multipleKeyframesSelected() const
|
||||
bool AbstractScrollGraphicsScene::multipleKeyframesSelected() const
|
||||
{
|
||||
return m_selectedKeyframes.count() > 1;
|
||||
}
|
||||
@@ -456,19 +456,19 @@ void TimelineGraphicsScene::invalidateRecordButtonsStatus()
|
||||
TimelinePropertyItem::updateRecordButtonStatus(item);
|
||||
}
|
||||
|
||||
int TimelineGraphicsScene::scrollOffset() const
|
||||
int AbstractScrollGraphicsScene::scrollOffset() const
|
||||
{
|
||||
return m_scrollOffset;
|
||||
}
|
||||
|
||||
void TimelineGraphicsScene::setScrollOffset(int offset)
|
||||
void AbstractScrollGraphicsScene::setScrollOffset(int offset)
|
||||
{
|
||||
m_scrollOffset = offset;
|
||||
emitScrollOffsetChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
QGraphicsView *TimelineGraphicsScene::graphicsView() const
|
||||
QGraphicsView *AbstractScrollGraphicsScene::graphicsView() const
|
||||
{
|
||||
for (auto *v : views())
|
||||
if (v->objectName() == "SceneView")
|
||||
@@ -477,7 +477,7 @@ QGraphicsView *TimelineGraphicsScene::graphicsView() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QGraphicsView *TimelineGraphicsScene::rulerView() const
|
||||
QGraphicsView *AbstractScrollGraphicsScene::rulerView() const
|
||||
{
|
||||
for (auto *v : views())
|
||||
if (v->objectName() == "RulerView")
|
||||
@@ -491,7 +491,7 @@ QmlTimeline TimelineGraphicsScene::currentTimeline() const
|
||||
return QmlTimeline(timelineModelNode());
|
||||
}
|
||||
|
||||
QRectF TimelineGraphicsScene::selectionBounds() const
|
||||
QRectF AbstractScrollGraphicsScene::selectionBounds() const
|
||||
{
|
||||
QRectF bbox;
|
||||
|
||||
@@ -501,7 +501,7 @@ QRectF TimelineGraphicsScene::selectionBounds() const
|
||||
return bbox;
|
||||
}
|
||||
|
||||
void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode,
|
||||
void AbstractScrollGraphicsScene::selectKeyframes(const SelectionMode &mode,
|
||||
const QList<TimelineKeyframeItem *> &items)
|
||||
{
|
||||
if (mode == SelectionMode::Remove || mode == SelectionMode::Toggle) {
|
||||
@@ -536,13 +536,14 @@ void TimelineGraphicsScene::selectKeyframes(const SelectionMode &mode,
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
void TimelineGraphicsScene::clearSelection()
|
||||
void AbstractScrollGraphicsScene::clearSelection()
|
||||
{
|
||||
for (auto *keyframe : m_selectedKeyframes)
|
||||
if (keyframe)
|
||||
keyframe->setHighlighted(false);
|
||||
|
||||
m_selectedKeyframes.clear();
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
QList<QGraphicsItem *> TimelineGraphicsScene::itemsAt(const QPointF &pos)
|
||||
@@ -682,7 +683,7 @@ ModelNode TimelineGraphicsScene::timelineModelNode() const
|
||||
void TimelineGraphicsScene::handleKeyframeDeletion()
|
||||
{
|
||||
QList<ModelNode> nodesToBeDeleted;
|
||||
for (auto keyframe : m_selectedKeyframes) {
|
||||
for (auto keyframe : selectedKeyframes()) {
|
||||
nodesToBeDeleted.append(keyframe->frameNode());
|
||||
}
|
||||
deleteKeyframes(nodesToBeDeleted);
|
||||
@@ -711,7 +712,7 @@ void TimelineGraphicsScene::pasteKeyframesToTarget(const ModelNode &targetNode)
|
||||
void TimelineGraphicsScene::copySelectedKeyframes()
|
||||
{
|
||||
TimelineActions::copyKeyframes(
|
||||
Utils::transform(m_selectedKeyframes, &TimelineKeyframeItem::frameNode));
|
||||
Utils::transform(selectedKeyframes(), &TimelineKeyframeItem::frameNode));
|
||||
}
|
||||
|
||||
void TimelineGraphicsScene::pasteSelectedKeyframes()
|
||||
@@ -756,7 +757,20 @@ void TimelineGraphicsScene::activateLayout()
|
||||
m_layout->activate();
|
||||
}
|
||||
|
||||
void TimelineGraphicsScene::emitScrollOffsetChanged()
|
||||
AbstractView *TimelineGraphicsScene::abstractView() const
|
||||
{
|
||||
return timelineView();
|
||||
}
|
||||
|
||||
int AbstractScrollGraphicsScene::getScrollOffset(QGraphicsScene *scene)
|
||||
{
|
||||
auto scrollScene = qobject_cast<AbstractScrollGraphicsScene*>(scene);
|
||||
if (scrollScene)
|
||||
return scrollScene->scrollOffset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AbstractScrollGraphicsScene::emitScrollOffsetChanged()
|
||||
{
|
||||
for (QGraphicsItem *item : items())
|
||||
TimelineMovableAbstractItem::emitScrollOffsetChanged(item);
|
||||
@@ -783,4 +797,8 @@ bool TimelineGraphicsScene::event(QEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
QmlDesigner::AbstractScrollGraphicsScene::AbstractScrollGraphicsScene(QWidget *parent)
|
||||
: QGraphicsScene(parent)
|
||||
{}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -36,6 +36,7 @@
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QGraphicsLinearLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QComboBox)
|
||||
QT_FORWARD_DECLARE_CLASS(QWidget)
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -51,15 +52,66 @@ class TimelinePlaceholder;
|
||||
class TimelineGraphicsLayout;
|
||||
class TimelineToolBar;
|
||||
|
||||
class TimelineGraphicsScene : public QGraphicsScene
|
||||
class AbstractScrollGraphicsScene : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void selectionChanged();
|
||||
public:
|
||||
AbstractScrollGraphicsScene(QWidget *parent);
|
||||
;
|
||||
|
||||
int scrollOffset() const;
|
||||
void setScrollOffset(int offset);
|
||||
static int getScrollOffset(QGraphicsScene *scene);
|
||||
|
||||
QRectF selectionBounds() const;
|
||||
|
||||
void selectKeyframes(const SelectionMode &mode, const QList<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);
|
||||
|
||||
private:
|
||||
void emitScrollOffsetChanged();
|
||||
|
||||
int m_scrollOffset = 0;
|
||||
QList<TimelineKeyframeItem *> m_selectedKeyframes;
|
||||
};
|
||||
|
||||
class TimelineGraphicsScene : public AbstractScrollGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TimelineGraphicsScene(TimelineWidget *parent);
|
||||
|
||||
@@ -74,7 +126,7 @@ public:
|
||||
|
||||
void invalidateLayout();
|
||||
qreal setCurrenFrame(const QmlTimeline &timeline, qreal frame);
|
||||
void setCurrentFrame(int frame);
|
||||
void setCurrentFrame(int frame) override;
|
||||
void setStartFrame(int frame);
|
||||
void setEndFrame(int frame);
|
||||
|
||||
@@ -82,11 +134,12 @@ public:
|
||||
TimelineWidget *timelineWidget() const;
|
||||
TimelineToolBar *toolBar() const;
|
||||
|
||||
qreal rulerScaling() const;
|
||||
int rulerWidth() const;
|
||||
qreal rulerDuration() const;
|
||||
qreal startFrame() const;
|
||||
qreal endFrame() const;
|
||||
qreal rulerScaling() const override;
|
||||
int rulerWidth() const override;
|
||||
qreal rulerDuration() const override;
|
||||
|
||||
qreal startFrame() const override;
|
||||
qreal endFrame() const override;
|
||||
|
||||
void updateKeyframePositionsCache();
|
||||
|
||||
@@ -97,39 +150,22 @@ public:
|
||||
QVector<qreal> keyframePositions() 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 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 invalidateKeyframesForTarget(const ModelNode &modelNode);
|
||||
|
||||
void invalidateScene();
|
||||
void invalidateScrollbar();
|
||||
void invalidateScrollbar() override;
|
||||
void invalidateCurrentValues();
|
||||
void invalidateRecordButtonsStatus();
|
||||
|
||||
int scrollOffset() const;
|
||||
void setScrollOffset(int offset);
|
||||
QGraphicsView *graphicsView() const;
|
||||
QGraphicsView *rulerView() const;
|
||||
|
||||
QmlTimeline currentTimeline() const;
|
||||
|
||||
QRectF selectionBounds() const;
|
||||
|
||||
void selectKeyframes(const SelectionMode &mode, const QList<TimelineKeyframeItem *> &items);
|
||||
void clearSelection();
|
||||
|
||||
void handleKeyframeDeletion();
|
||||
void deleteAllKeyframesForTarget(const ModelNode &targetNode);
|
||||
void insertAllKeyframesForTarget(const ModelNode &targetNode);
|
||||
@@ -143,8 +179,7 @@ public:
|
||||
|
||||
void activateLayout();
|
||||
|
||||
signals:
|
||||
void statusBarMessageChanged(const QString &message);
|
||||
AbstractView *abstractView() const override;
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
@@ -163,7 +198,6 @@ private:
|
||||
void invalidateSections();
|
||||
ModelNode timelineModelNode() const;
|
||||
|
||||
void emitScrollOffsetChanged();
|
||||
void emitStatusBarPlayheadFrameChanged(int frame);
|
||||
|
||||
QList<QGraphicsItem *> itemsAt(const QPointF &pos);
|
||||
@@ -178,12 +212,8 @@ private:
|
||||
|
||||
TimelineToolDelegate m_tools;
|
||||
|
||||
QList<TimelineKeyframeItem *> m_selectedKeyframes;
|
||||
|
||||
// sorted, unique cache of keyframes positions, used for snapping
|
||||
QVector<qreal> m_keyframePositionsCache;
|
||||
|
||||
int m_scrollOffset = 0;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -52,8 +52,7 @@ TimelineItem::TimelineItem(TimelineItem *parent)
|
||||
|
||||
TimelineGraphicsScene *TimelineItem::timelineScene() const
|
||||
{
|
||||
return static_cast<TimelineGraphicsScene *>(scene());
|
||||
;
|
||||
return qobject_cast<TimelineGraphicsScene *>(scene());
|
||||
}
|
||||
|
||||
TimelineFrameHandle::TimelineFrameHandle(TimelineItem *parent)
|
||||
@@ -93,7 +92,7 @@ void TimelineFrameHandle::setPosition(qreal frame)
|
||||
|
||||
void TimelineFrameHandle::setPositionInteractive(const QPointF &position)
|
||||
{
|
||||
const double width = timelineScene()->width();
|
||||
const double width = abstractScrollGraphicsScene()->width();
|
||||
|
||||
if (position.x() > width) {
|
||||
callSetClampedXPosition(width - (rect().width() / 2) - 1);
|
||||
@@ -104,7 +103,7 @@ void TimelineFrameHandle::setPositionInteractive(const QPointF &position)
|
||||
} else {
|
||||
callSetClampedXPosition(position.x() - rect().width() / 2);
|
||||
const qreal frame = std::round(mapFromSceneToFrame(rect().center().x()));
|
||||
timelineScene()->commitCurrentFrame(frame);
|
||||
timelineGraphicsScene()->commitCurrentFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +127,11 @@ TimelineFrameHandle *TimelineFrameHandle::asTimelineFrameHandle()
|
||||
return this;
|
||||
}
|
||||
|
||||
TimelineGraphicsScene *TimelineFrameHandle::timelineGraphicsScene() const
|
||||
{
|
||||
return qobject_cast<TimelineGraphicsScene* >(abstractScrollGraphicsScene());
|
||||
}
|
||||
|
||||
void TimelineFrameHandle::scrollOffsetChanged()
|
||||
{
|
||||
setPosition(position());
|
||||
@@ -182,7 +186,7 @@ void TimelineFrameHandle::paint(QPainter *painter,
|
||||
|
||||
QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const
|
||||
{
|
||||
for (auto *view : timelineScene()->views()) {
|
||||
for (auto *view : abstractScrollGraphicsScene()->views()) {
|
||||
if (view->objectName() == "SceneView") {
|
||||
auto graphicsViewCoords = view->mapFromGlobal(pos);
|
||||
auto sceneCoords = view->mapToScene(graphicsViewCoords);
|
||||
@@ -195,7 +199,7 @@ QPointF TimelineFrameHandle::mapFromGlobal(const QPoint &pos) const
|
||||
int TimelineFrameHandle::computeScrollSpeed() const
|
||||
{
|
||||
const double mouse = mapFromGlobal(QCursor::pos()).x();
|
||||
const double width = timelineScene()->width();
|
||||
const double width = abstractScrollGraphicsScene()->width();
|
||||
|
||||
const double acc = mouse > width ? mouse - width
|
||||
: double(TimelineConstants::sectionWidth) - mouse;
|
||||
@@ -216,7 +220,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x)
|
||||
const int minimumWidth = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
|
||||
- rect().width() / 2;
|
||||
const int maximumWidth = minimumWidth
|
||||
+ timelineScene()->rulerDuration() * timelineScene()->rulerScaling()
|
||||
+ abstractScrollGraphicsScene()->rulerDuration() * abstractScrollGraphicsScene()->rulerScaling()
|
||||
- scrollOffset();
|
||||
|
||||
setClampedXPosition(x, minimumWidth, maximumWidth);
|
||||
@@ -225,7 +229,7 @@ void TimelineFrameHandle::callSetClampedXPosition(double x)
|
||||
// Auto scroll when dragging playhead out of bounds.
|
||||
void TimelineFrameHandle::scrollOutOfBounds()
|
||||
{
|
||||
const double width = timelineScene()->width();
|
||||
const double width = abstractScrollGraphicsScene()->width();
|
||||
const double mouse = mapFromGlobal(QCursor::pos()).x();
|
||||
|
||||
if (mouse > width)
|
||||
@@ -236,14 +240,14 @@ void TimelineFrameHandle::scrollOutOfBounds()
|
||||
|
||||
void TimelineFrameHandle::scrollOutOfBoundsMax()
|
||||
{
|
||||
const double width = timelineScene()->width();
|
||||
const double width = abstractScrollGraphicsScene()->width();
|
||||
if (QApplication::mouseButtons() == Qt::LeftButton) {
|
||||
const double frameWidth = timelineScene()->rulerScaling();
|
||||
const double frameWidth = abstractScrollGraphicsScene()->rulerScaling();
|
||||
const double upperThreshold = width - frameWidth;
|
||||
|
||||
if (rect().center().x() > upperThreshold) {
|
||||
timelineScene()->setScrollOffset(computeScrollSpeed());
|
||||
timelineScene()->invalidateScrollbar();
|
||||
abstractScrollGraphicsScene()->setScrollOffset(computeScrollSpeed());
|
||||
abstractScrollGraphicsScene()->invalidateScrollbar();
|
||||
}
|
||||
|
||||
callSetClampedXPosition(width - (rect().width() / 2) - 1);
|
||||
@@ -253,8 +257,8 @@ void TimelineFrameHandle::scrollOutOfBoundsMax()
|
||||
callSetClampedXPosition(width - (rect().width() / 2) - 1);
|
||||
|
||||
const int frame = std::floor(mapFromSceneToFrame(rect().center().x()));
|
||||
const int ef = timelineScene()->endFrame();
|
||||
timelineScene()->commitCurrentFrame(frame <= ef ? frame : ef);
|
||||
const int ef = abstractScrollGraphicsScene()->endFrame();
|
||||
timelineGraphicsScene()->commitCurrentFrame(frame <= ef ? frame : ef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,11 +268,11 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
|
||||
auto offset = computeScrollSpeed();
|
||||
|
||||
if (offset >= 0)
|
||||
timelineScene()->setScrollOffset(offset);
|
||||
abstractScrollGraphicsScene()->setScrollOffset(offset);
|
||||
else
|
||||
timelineScene()->setScrollOffset(0);
|
||||
abstractScrollGraphicsScene()->setScrollOffset(0);
|
||||
|
||||
timelineScene()->invalidateScrollbar();
|
||||
abstractScrollGraphicsScene()->invalidateScrollbar();
|
||||
|
||||
callSetClampedXPosition(TimelineConstants::sectionWidth);
|
||||
m_timer.start();
|
||||
@@ -278,7 +282,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
|
||||
|
||||
int frame = mapFromSceneToFrame(rect().center().x());
|
||||
|
||||
const int sframe = timelineScene()->startFrame();
|
||||
const int sframe = abstractScrollGraphicsScene()->startFrame();
|
||||
if (frame != sframe) {
|
||||
const qreal framePos = mapFromFrameToScene(frame);
|
||||
|
||||
@@ -287,7 +291,7 @@ void TimelineFrameHandle::scrollOutOfBoundsMin()
|
||||
frame++;
|
||||
}
|
||||
|
||||
timelineScene()->commitCurrentFrame(frame >= sframe ? frame : sframe);
|
||||
timelineGraphicsScene()->commitCurrentFrame(frame >= sframe ? frame : sframe);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,8 @@ QT_FORWARD_DECLARE_CLASS(QPainterPath)
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class TimelineGraphicsScene;
|
||||
|
||||
class TimelineItem : public QGraphicsWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -59,6 +61,8 @@ public:
|
||||
|
||||
TimelineFrameHandle *asTimelineFrameHandle() override;
|
||||
|
||||
TimelineGraphicsScene *timelineGraphicsScene() const;
|
||||
|
||||
protected:
|
||||
void scrollOffsetChanged() override;
|
||||
QPainterPath shape() const override;
|
||||
|
@@ -53,7 +53,7 @@ void TimelineMovableAbstractItem::itemDoubleClicked()
|
||||
|
||||
int TimelineMovableAbstractItem::scrollOffset() const
|
||||
{
|
||||
return timelineScene()->scrollOffset();
|
||||
return abstractScrollGraphicsScene()->scrollOffset();
|
||||
}
|
||||
|
||||
int TimelineMovableAbstractItem::xPosScrollOffset(int x) const
|
||||
@@ -63,7 +63,7 @@ int TimelineMovableAbstractItem::xPosScrollOffset(int x) const
|
||||
|
||||
qreal TimelineMovableAbstractItem::mapFromFrameToScene(qreal x) const
|
||||
{
|
||||
return TimelineConstants::sectionWidth + (x - timelineScene()->startFrame()) * rulerScaling()
|
||||
return TimelineConstants::sectionWidth + (x - abstractScrollGraphicsScene()->startFrame()) * rulerScaling()
|
||||
- scrollOffset() + TimelineConstants::timelineLeftOffset;
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ qreal TimelineMovableAbstractItem::mapFromSceneToFrame(qreal x) const
|
||||
{
|
||||
return xPosScrollOffset(x - TimelineConstants::sectionWidth
|
||||
- TimelineConstants::timelineLeftOffset)
|
||||
/ timelineScene()->rulerScaling()
|
||||
+ timelineScene()->startFrame();
|
||||
/ abstractScrollGraphicsScene()->rulerScaling()
|
||||
+ abstractScrollGraphicsScene()->startFrame();
|
||||
}
|
||||
|
||||
void TimelineMovableAbstractItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
@@ -133,7 +133,7 @@ TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem(QGraph
|
||||
|
||||
qreal TimelineMovableAbstractItem::rulerScaling() const
|
||||
{
|
||||
return static_cast<TimelineGraphicsScene *>(scene())->rulerScaling();
|
||||
return qobject_cast<AbstractScrollGraphicsScene *>(scene())->rulerScaling();
|
||||
}
|
||||
|
||||
int TimelineMovableAbstractItem::type() const
|
||||
@@ -141,9 +141,9 @@ int TimelineMovableAbstractItem::type() const
|
||||
return Type;
|
||||
}
|
||||
|
||||
TimelineGraphicsScene *TimelineMovableAbstractItem::timelineScene() const
|
||||
AbstractScrollGraphicsScene *TimelineMovableAbstractItem::abstractScrollGraphicsScene() const
|
||||
{
|
||||
return static_cast<TimelineGraphicsScene *>(scene());
|
||||
return qobject_cast<AbstractScrollGraphicsScene *>(scene());
|
||||
}
|
||||
|
||||
TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem()
|
||||
|
@@ -32,7 +32,7 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class TimelineGraphicsScene;
|
||||
class AbstractScrollGraphicsScene;
|
||||
class TimelineKeyframeItem;
|
||||
class TimelineFrameHandle;
|
||||
|
||||
@@ -75,7 +75,7 @@ protected:
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
void setClampedXPosition(qreal x, qreal min, qreal max);
|
||||
TimelineGraphicsScene *timelineScene() const;
|
||||
AbstractScrollGraphicsScene *abstractScrollGraphicsScene() const;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -61,7 +61,7 @@ QPointF mapToItem(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *e
|
||||
return event->scenePos();
|
||||
}
|
||||
|
||||
TimelineMoveTool::TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate)
|
||||
TimelineMoveTool::TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate)
|
||||
: TimelineAbstractTool(scene, delegate)
|
||||
{}
|
||||
|
||||
@@ -155,7 +155,7 @@ void TimelineMoveTool::mouseReleaseEvent(TimelineMovableAbstractItem *item,
|
||||
}
|
||||
}
|
||||
|
||||
scene()->timelineView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent",
|
||||
scene()->abstractView()->executeInTransaction("TimelineMoveTool::mouseReleaseEvent",
|
||||
[this, current]() {
|
||||
current->commitPosition(mapToItem(current, current->rect().center()));
|
||||
|
||||
|
@@ -41,7 +41,7 @@ class TimelineMoveTool : public TimelineAbstractTool
|
||||
Q_DECLARE_TR_FUNCTIONS(TimelineMoveTool)
|
||||
|
||||
public:
|
||||
explicit TimelineMoveTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
explicit TimelineMoveTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
void mousePressEvent(TimelineMovableAbstractItem *item,
|
||||
QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseMoveEvent(TimelineMovableAbstractItem *item, QGraphicsSceneMouseEvent *event) override;
|
||||
|
@@ -505,7 +505,7 @@ TimelineKeyframeItem::TimelineKeyframeItem(TimelinePropertyItem *parent, const M
|
||||
|
||||
TimelineKeyframeItem::~TimelineKeyframeItem()
|
||||
{
|
||||
timelineScene()->selectKeyframes(SelectionMode::Remove, {this});
|
||||
abstractScrollGraphicsScene()->selectKeyframes(SelectionMode::Remove, {this});
|
||||
}
|
||||
|
||||
void TimelineKeyframeItem::updateFrame()
|
||||
@@ -555,8 +555,8 @@ void TimelineKeyframeItem::commitPosition(const QPointF &point)
|
||||
|
||||
void TimelineKeyframeItem::itemDoubleClicked()
|
||||
{
|
||||
std::pair<qreal, qreal> timelineRange = {timelineScene()->currentTimeline().startKeyframe(),
|
||||
timelineScene()->currentTimeline().endKeyframe()};
|
||||
std::pair<qreal, qreal> timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(),
|
||||
timelineGraphicsScene()->currentTimeline().endKeyframe()};
|
||||
editValue(m_frame, timelineRange, propertyItem()->propertyName());
|
||||
}
|
||||
|
||||
@@ -565,6 +565,11 @@ TimelineKeyframeItem *TimelineKeyframeItem::asTimelineKeyframeItem()
|
||||
return this;
|
||||
}
|
||||
|
||||
TimelineGraphicsScene *TimelineKeyframeItem::timelineGraphicsScene() const
|
||||
{
|
||||
return qobject_cast<TimelineGraphicsScene *>(abstractScrollGraphicsScene());
|
||||
}
|
||||
|
||||
void TimelineKeyframeItem::blockUpdates()
|
||||
{
|
||||
s_blockUpdates = true;
|
||||
@@ -643,21 +648,21 @@ void TimelineKeyframeItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *even
|
||||
QMenu mainMenu;
|
||||
QAction *removeAction = mainMenu.addAction(tr("Delete Keyframe"));
|
||||
QObject::connect(removeAction, &QAction::triggered, [this]() {
|
||||
timelineScene()->handleKeyframeDeletion();
|
||||
timelineGraphicsScene()->handleKeyframeDeletion();
|
||||
});
|
||||
|
||||
QAction *editEasingAction = mainMenu.addAction(tr("Edit Easing Curve..."));
|
||||
QObject::connect(editEasingAction, &QAction::triggered, [this]() {
|
||||
const QList<ModelNode> keys = Utils::transform(timelineScene()->selectedKeyframes(),
|
||||
const QList<ModelNode> keys = Utils::transform(abstractScrollGraphicsScene()->selectedKeyframes(),
|
||||
&TimelineKeyframeItem::m_frame);
|
||||
|
||||
setEasingCurve(timelineScene(), keys);
|
||||
setEasingCurve(timelineGraphicsScene(), keys);
|
||||
});
|
||||
|
||||
QAction *editValueAction = mainMenu.addAction(tr("Edit Keyframe..."));
|
||||
QObject::connect(editValueAction, &QAction::triggered, [this]() {
|
||||
std::pair<qreal, qreal> timelineRange = {timelineScene()->currentTimeline().startKeyframe(),
|
||||
timelineScene()->currentTimeline().endKeyframe()};
|
||||
std::pair<qreal, qreal> timelineRange = {timelineGraphicsScene()->currentTimeline().startKeyframe(),
|
||||
timelineGraphicsScene()->currentTimeline().endKeyframe()};
|
||||
editValue(m_frame, timelineRange, propertyItem()->propertyName());
|
||||
});
|
||||
|
||||
|
@@ -67,6 +67,7 @@ public:
|
||||
void itemDoubleClicked() override;
|
||||
|
||||
TimelineKeyframeItem *asTimelineKeyframeItem() override;
|
||||
TimelineGraphicsScene *timelineGraphicsScene() const;
|
||||
|
||||
protected:
|
||||
bool hasManualBezier() const;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/****************************************************************************
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
@@ -215,15 +215,7 @@ QVector<qreal> TimelineSectionItem::keyframePositions() const
|
||||
return out;
|
||||
}
|
||||
|
||||
QTransform rotatationTransform(qreal degrees)
|
||||
{
|
||||
QTransform transform;
|
||||
transform.rotate(degrees);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
QPixmap rotateby90(const QPixmap &pixmap)
|
||||
static QPixmap rotateby90(const QPixmap &pixmap)
|
||||
{
|
||||
QImage sourceImage = pixmap.toImage();
|
||||
QImage destImage(pixmap.height(), pixmap.width(), sourceImage.format());
|
||||
@@ -550,6 +542,13 @@ void TimelineRulerSectionItem::invalidateRulerSize(const QmlTimeline &timeline)
|
||||
m_end = timeline.endKeyframe();
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::invalidateRulerSize(const qreal length)
|
||||
{
|
||||
m_duration = length;
|
||||
m_start = 0;
|
||||
m_end = length;
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::setRulerScaleFactor(int scaling)
|
||||
{
|
||||
qreal blend = qreal(scaling) / 100.0;
|
||||
@@ -627,10 +626,12 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi
|
||||
static const QColor highlightColor = Theme::instance()->Theme::qmlDesignerButtonColor();
|
||||
static const QColor handleColor = Theme::getColor(Theme::QmlDesigner_HighlightColor);
|
||||
|
||||
const int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene());
|
||||
|
||||
painter->save();
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->translate(-timelineScene()->scrollOffset(), 0);
|
||||
painter->translate(-scrollOffset, 0);
|
||||
painter->fillRect(TimelineConstants::sectionWidth,
|
||||
0,
|
||||
size().width() - TimelineConstants::sectionWidth,
|
||||
@@ -666,11 +667,13 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi
|
||||
|
||||
const int height = size().height() - 1;
|
||||
|
||||
|
||||
|
||||
drawLine(painter,
|
||||
TimelineConstants::sectionWidth + timelineScene()->scrollOffset()
|
||||
TimelineConstants::sectionWidth + scrollOffset
|
||||
- TimelineConstants::timelineLeftOffset,
|
||||
height,
|
||||
size().width() + timelineScene()->scrollOffset(),
|
||||
size().width() + scrollOffset,
|
||||
height);
|
||||
|
||||
QFont font = painter->font();
|
||||
@@ -720,9 +723,12 @@ void TimelineRulerSectionItem::paintTicks(QPainter *painter)
|
||||
|
||||
m_frameTick = qreal(deltaLine);
|
||||
|
||||
int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene());
|
||||
|
||||
int height = size().height();
|
||||
const int totalWidth = (size().width() + timelineScene()->scrollOffset()) / m_scaling;
|
||||
for (int i = timelineScene()->scrollOffset() / m_scaling; i < totalWidth; ++i) {
|
||||
const int totalWidth = (size().width() + scrollOffset) / m_scaling;
|
||||
|
||||
for (int i = scrollOffset / m_scaling; i < totalWidth; ++i) {
|
||||
if ((i % deltaText) == 0) {
|
||||
drawCenteredText(painter,
|
||||
TimelineConstants::sectionWidth + i * m_scaling,
|
||||
@@ -794,11 +800,11 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end)
|
||||
|
||||
qreal min = qreal(TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
|
||||
- scrollOffset());
|
||||
qreal max = qreal(timelineScene()->rulerWidth() - TimelineConstants::sectionWidth
|
||||
qreal max = qreal(abstractScrollGraphicsScene()->rulerWidth() - TimelineConstants::sectionWidth
|
||||
+ rect().width());
|
||||
|
||||
const qreal minFrameX = mapFromFrameToScene(timelineScene()->startFrame());
|
||||
const qreal maxFrameX = mapFromFrameToScene(timelineScene()->endFrame());
|
||||
const qreal minFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->startFrame() - 1);
|
||||
const qreal maxFrameX = mapFromFrameToScene(abstractScrollGraphicsScene()->endFrame()+ 1000);
|
||||
|
||||
if (min < minFrameX)
|
||||
min = minFrameX;
|
||||
@@ -811,7 +817,7 @@ void TimelineBarItem::itemMoved(const QPointF &start, const QPointF &end)
|
||||
else
|
||||
dragHandle(rect(), end, min, max);
|
||||
|
||||
timelineScene()->statusBarMessageChanged(
|
||||
abstractScrollGraphicsScene()->statusBarMessageChanged(
|
||||
tr("Range from %1 to %2")
|
||||
.arg(qRound(mapFromSceneToFrame(rect().x())))
|
||||
.arg(qRound(mapFromSceneToFrame(rect().width() + rect().x()))));
|
||||
@@ -975,7 +981,7 @@ void TimelineBarItem::dragCenter(QRectF rect, const QPointF &pos, qreal min, qre
|
||||
if (validateBounds(pos.x() - rect.topLeft().x())) {
|
||||
qreal targetX = pos.x() - m_pivot;
|
||||
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
|
||||
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX));
|
||||
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
|
||||
targetX = mapFromFrameToScene(snappedTargetFrame);
|
||||
}
|
||||
rect.moveLeft(targetX);
|
||||
@@ -999,7 +1005,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre
|
||||
if (validateBounds(pos.x() - left.topLeft().x())) {
|
||||
qreal targetX = pos.x() - m_pivot;
|
||||
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
|
||||
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX));
|
||||
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
|
||||
targetX = mapFromFrameToScene(snappedTargetFrame);
|
||||
}
|
||||
rect.setLeft(targetX);
|
||||
@@ -1015,7 +1021,7 @@ void TimelineBarItem::dragHandle(QRectF rect, const QPointF &pos, qreal min, qre
|
||||
if (validateBounds(pos.x() - right.topRight().x())) {
|
||||
qreal targetX = pos.x() - m_pivot;
|
||||
if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { // snapping
|
||||
qreal snappedTargetFrame = timelineScene()->snap(mapFromSceneToFrame(targetX));
|
||||
qreal snappedTargetFrame = abstractScrollGraphicsScene()->snap(mapFromSceneToFrame(targetX));
|
||||
targetX = mapFromFrameToScene(snappedTargetFrame);
|
||||
}
|
||||
rect.setRight(targetX);
|
||||
|
@@ -151,6 +151,7 @@ public:
|
||||
static TimelineRulerSectionItem *create(QGraphicsScene *parentScene, TimelineItem *parent);
|
||||
|
||||
void invalidateRulerSize(const QmlTimeline &timeline);
|
||||
void invalidateRulerSize(const qreal length);
|
||||
|
||||
void setRulerScaleFactor(int scaling);
|
||||
|
||||
|
@@ -38,7 +38,7 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
TimelineSelectionTool::TimelineSelectionTool(TimelineGraphicsScene *scene,
|
||||
TimelineSelectionTool::TimelineSelectionTool(AbstractScrollGraphicsScene *scene,
|
||||
TimelineToolDelegate *delegate)
|
||||
: TimelineAbstractTool(scene, delegate)
|
||||
, m_selectionRect(new QGraphicsRectItem)
|
||||
|
@@ -45,7 +45,7 @@ enum class SelectionMode { New, Add, Remove, Toggle };
|
||||
class TimelineSelectionTool : public TimelineAbstractTool
|
||||
{
|
||||
public:
|
||||
explicit TimelineSelectionTool(TimelineGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
explicit TimelineSelectionTool(AbstractScrollGraphicsScene *scene, TimelineToolDelegate *delegate);
|
||||
|
||||
~TimelineSelectionTool() override;
|
||||
|
||||
|
@@ -56,19 +56,19 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
bool isSpacer(QObject *object)
|
||||
static bool isSpacer(QObject *object)
|
||||
{
|
||||
return object->property("spacer_widget").toBool();
|
||||
}
|
||||
|
||||
QWidget *createSpacer()
|
||||
static QWidget *createSpacer()
|
||||
{
|
||||
QWidget *spacer = new QWidget();
|
||||
spacer->setProperty("spacer_widget", true);
|
||||
return spacer;
|
||||
}
|
||||
|
||||
int controlWidth(QToolBar *bar, QObject *control)
|
||||
static int controlWidth(QToolBar *bar, QObject *control)
|
||||
{
|
||||
QWidget *widget = nullptr;
|
||||
|
||||
@@ -84,7 +84,7 @@ int controlWidth(QToolBar *bar, QObject *control)
|
||||
return 0;
|
||||
}
|
||||
|
||||
QAction *createAction(const Core::Id &id,
|
||||
static QAction *createAction(const Core::Id &id,
|
||||
const QIcon &icon,
|
||||
const QString &name,
|
||||
const QKeySequence &shortcut)
|
||||
|
@@ -38,7 +38,7 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
TimelineToolDelegate::TimelineToolDelegate(TimelineGraphicsScene *scene)
|
||||
TimelineToolDelegate::TimelineToolDelegate(AbstractScrollGraphicsScene *scene)
|
||||
: m_scene(scene)
|
||||
, m_start()
|
||||
, m_moveTool(new TimelineMoveTool(scene, this))
|
||||
|
@@ -38,7 +38,7 @@ class TimelineGraphicsScene;
|
||||
class TimelineToolDelegate
|
||||
{
|
||||
public:
|
||||
TimelineToolDelegate(TimelineGraphicsScene* scene);
|
||||
TimelineToolDelegate(AbstractScrollGraphicsScene* scene);
|
||||
|
||||
QPointF startPoint() const;
|
||||
|
||||
@@ -65,7 +65,7 @@ private:
|
||||
private:
|
||||
static const int dragDistance = 20;
|
||||
|
||||
TimelineGraphicsScene* m_scene;
|
||||
AbstractScrollGraphicsScene* m_scene;
|
||||
|
||||
QPointF m_start;
|
||||
|
||||
|
@@ -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
|
@@ -0,0 +1,4 @@
|
||||
<RCC>
|
||||
<qresource prefix="/transitioneditor">
|
||||
</qresource>
|
||||
</RCC>
|
@@ -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
|
@@ -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 ¶llel : transition.directSubModelNodes()) {
|
||||
auto item = TransitionEditorSectionItem::create(parallel, this);
|
||||
m_layout->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
m_placeholder2->setParentItem(this);
|
||||
m_layout->addItem(m_placeholder2);
|
||||
|
||||
if (auto *scene = timelineScene())
|
||||
if (auto *view = scene->timelineView())
|
||||
if (!transition.isValid() && view->isAttached())
|
||||
emit scaleFactorChanged(0);
|
||||
}
|
||||
|
||||
void TransitionEditorGraphicsLayout::setDuration(qreal duration)
|
||||
{
|
||||
m_rulerItem->invalidateRulerSize(duration);
|
||||
}
|
||||
|
||||
void TransitionEditorGraphicsLayout::setRulerScaleFactor(int factor)
|
||||
{
|
||||
m_rulerItem->setRulerScaleFactor(factor);
|
||||
}
|
||||
|
||||
void TransitionEditorGraphicsLayout::invalidate()
|
||||
{
|
||||
m_layout->invalidate();
|
||||
}
|
||||
|
||||
int TransitionEditorGraphicsLayout::maximumScrollValue() const
|
||||
{
|
||||
const qreal w = this->geometry().width() - qreal(TimelineConstants::sectionWidth);
|
||||
const qreal duration = m_rulerItem->rulerDuration() + m_rulerItem->rulerDuration() * 0.1;
|
||||
const qreal maxr = m_rulerItem->rulerScaling() * duration - w;
|
||||
return std::round(qMax(maxr, 0.0));
|
||||
}
|
||||
|
||||
void TransitionEditorGraphicsLayout::activate()
|
||||
{
|
||||
m_layout->activate();
|
||||
}
|
||||
|
||||
TimelineRulerSectionItem *TransitionEditorGraphicsLayout::ruler() const
|
||||
{
|
||||
return m_rulerItem;
|
||||
}
|
||||
|
||||
} // End namespace QmlDesigner.
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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>
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
Reference in New Issue
Block a user