forked from qt-creator/qt-creator
382 lines
13 KiB
C++
382 lines
13 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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 <qmldesignerconstants.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 (!isEnabled())
|
|
return;
|
|
|
|
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);
|
|
|
|
const ModelNode parent = parentProperty.parentModelNode();
|
|
if (parent.isValid() && parent.metaInfo().isSubclassOf("QtQuick.Transition"))
|
|
asyncUpdate(parent);
|
|
}
|
|
|
|
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();
|
|
|
|
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::auxiliaryDataChanged(const ModelNode &modelNode,
|
|
const PropertyName &name,
|
|
const QVariant &data)
|
|
{
|
|
if (name == QmlDesigner::lockedProperty && data.toBool() && modelNode.isValid()) {
|
|
for (const auto &node : modelNode.allSubModelNodesAndThisNode()) {
|
|
if (node.hasAuxiliaryData("transition_expanded"))
|
|
m_transitionEditorWidget->graphicsScene()->invalidateHeightForTarget(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TRANSITION_ADDED);
|
|
|
|
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", "float"};
|
|
|
|
for (const QmlModelState &state : qAsConst(states)) {
|
|
for (const QmlPropertyChanges & change : state.propertyChanges()) {
|
|
QStringList locList;
|
|
const ModelNode target = change.target();
|
|
if (target.isValid() && target.hasMetaInfo()) {
|
|
const QString targetId = target.id();
|
|
for (const VariantProperty &property : change.modelNode().variantProperties()) {
|
|
TypeName typeName = target.metaInfo().propertyTypeName(property.name());
|
|
if (typeName.startsWith("<cpp>."))
|
|
typeName.remove(0, 6);
|
|
|
|
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 (auto it = idPropertyList.cbegin(); it != idPropertyList.cend(); ++it) {
|
|
ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation",
|
|
2,
|
|
12);
|
|
transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation);
|
|
for (const QString &property : it.value()) {
|
|
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(it.key());
|
|
sequentialAnimation.defaultNodeAbstractProperty().reparentHere(
|
|
propertyAnimation);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
QString properties;
|
|
for (const PropertyName &property : validProperties)
|
|
properties.append(QString::fromUtf8(property) + ", ");
|
|
if (!properties.isEmpty())
|
|
properties.chop(2);
|
|
Core::AsynchronousMessageBox::warning(
|
|
tr("No Property Changes to Animate"),
|
|
tr("To add transitions, first change the properties that you want to animate in states (%1).")
|
|
.arg(properties));
|
|
}
|
|
|
|
if (m_transitionEditorWidget)
|
|
m_transitionEditorWidget->init();
|
|
|
|
return transition;
|
|
}
|
|
|
|
TransitionEditorWidget *TransitionEditorView::createWidget()
|
|
{
|
|
if (!m_transitionEditorWidget)
|
|
m_transitionEditorWidget = new TransitionEditorWidget(this);
|
|
|
|
auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
|
|
Core::ICore::addContextObject(transitionContext);
|
|
|
|
return m_transitionEditorWidget;
|
|
}
|
|
|
|
WidgetInfo TransitionEditorView::widgetInfo()
|
|
{
|
|
return createWidgetInfo(createWidget(),
|
|
"TransitionEditor",
|
|
WidgetInfo::BottomPane,
|
|
0,
|
|
tr("Transitions"));
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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
|