QmlDesigner: First implementation of TransitionEditor

Change-Id: I14391e872f6a257a2cdf75e7d577de64c384c1fd
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
This commit is contained in:
Thomas Hartmann
2020-06-17 19:10:35 +02:00
parent 4e9b6d68c1
commit 454ff4c46b
27 changed files with 4282 additions and 0 deletions

View File

@@ -668,6 +668,22 @@ extend_qtc_plugin(QmlDesigner
timelinewidget.cpp timelinewidget.h timelinewidget.cpp timelinewidget.h
) )
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/transitioneditor
SOURCES
transitioneditorview.cpp transitioneditorview.h
transitioneditorwidget.cpp transitioneditorwidget.h
transitioneditortoolbar.cpp transitioneditortoolbar.h
transitioneditorgraphicsscene.cpp transitioneditorgraphicsscene.h
transitioneditorgraphicslayout.cpp transitioneditorgraphicslayout.h
transitioneditorsectionitem.cpp transitioneditorsectionitem.h
transitioneditorpropertyitem.cpp transitioneditorpropertyitem.h
transitioneditorsettingsdialog.cpp transitioneditorsettingsdialog.h
transitioneditorsettingsdialog.ui
transitionform.cpp transitionform.h
transitioneditor.qrc
)
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/curveeditor SOURCES_PREFIX components/curveeditor
SOURCES SOURCES

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <qmltimeline.h>
#include <QDialog>
QT_FORWARD_DECLARE_CLASS(QSpinBox)
namespace QmlDesigner {
class TransitionForm;
class TransitionEditorView;
namespace Ui {
class TransitionEditorSettingsDialog;
}
class TransitionEditorSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit TransitionEditorSettingsDialog(QWidget *parent, class TransitionEditorView *view);
void setCurrentTransition(const ModelNode &timeline);
~TransitionEditorSettingsDialog() override;
protected:
void keyPressEvent(QKeyEvent *event) override;
private:
void setupTransitions(const ModelNode &node);
void addTransitionTab(const QmlTimeline &node);
Ui::TransitionEditorSettingsDialog *ui;
TransitionEditorView *m_transitionEditorView;
ModelNode m_currentTransition;
};
} // namespace QmlDesigner

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,103 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "timelineeditor/timelineutils.h"
#include <coreplugin/icontext.h>
#include <QWidget>
#include <functional>
QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QGraphicsView)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QResizeEvent)
QT_FORWARD_DECLARE_CLASS(QScrollBar)
QT_FORWARD_DECLARE_CLASS(QShowEvent)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QPushButton)
namespace QmlDesigner {
class TransitionEditorView;
class TransitionEditorToolBar;
class TransitionEditorGraphicsScene;
class ModelNode;
class TransitionEditorWidget : public QWidget
{
Q_OBJECT
public:
explicit TransitionEditorWidget(TransitionEditorView *view);
void contextHelp(const Core::IContext::HelpCallback &callback) const;
TransitionEditorGraphicsScene *graphicsScene() const;
TransitionEditorView *transitionEditorView() const;
TransitionEditorToolBar *toolBar() const;
void init();
void reset();
void setupScrollbar(int min, int max, int current);
void changeScaleFactor(int factor);
void updateData(const ModelNode &transition);
public slots:
void selectionChanged();
void scroll(const TimelineUtils::Side &side);
protected:
void showEvent(QShowEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
void connectToolbar();
void setTransitionActive(bool b);
void openEasingCurveEditor();
TransitionEditorToolBar *m_toolbar = nullptr;
QGraphicsView *m_rulerView = nullptr;
QGraphicsView *m_graphicsView = nullptr;
QScrollBar *m_scrollbar = nullptr;
QLabel *m_statusBar = nullptr;
TransitionEditorView *m_transitionEditorView = nullptr;
TransitionEditorGraphicsScene *m_graphicsScene;
QPushButton *m_addButton = nullptr;
QWidget *m_onboardingContainer = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,211 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitionform.h"
#include "timelineform.h"
#include "ui_transitionform.h"
#include <abstractview.h>
#include <bindingproperty.h>
#include <exception>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <qmlitemnode.h>
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace QmlDesigner {
TransitionForm::TransitionForm(QWidget *parent)
: QWidget(parent)
, ui(new Ui::TransitionForm)
{
ui->setupUi(this);
connect(ui->idLineEdit, &QLineEdit::editingFinished, [this]() {
QTC_ASSERT(m_transition.isValid(), return );
static QString lastString;
const QString newId = ui->idLineEdit->text();
if (newId == lastString)
return;
lastString = newId;
if (newId == m_transition.id())
return;
bool error = false;
if (!ModelNode::isValidId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
tr("%1 is an invalid id.").arg(newId));
error = true;
} else if (m_transition.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
tr("%1 already exists.").arg(newId));
error = true;
} else {
m_transition.setIdWithRefactoring(newId);
}
if (error) {
lastString.clear();
ui->idLineEdit->setText(m_transition.id());
}
});
connect(ui->listWidgetTo, &QListWidget::itemChanged, this, [this]() {
QTC_ASSERT(m_transition.isValid(), return );
const QmlItemNode root(m_transition.view()->rootModelNode());
QTC_ASSERT(root.isValid(), return );
const int stateCount = root.states().names().count();
QStringList stateNames;
for (const QListWidgetItem *item : ui->listWidgetTo->findItems("*", Qt::MatchWildcard)) {
if (item->checkState() == Qt::Checked)
stateNames.append(item->text());
}
QString toValue;
if (stateCount == stateNames.count())
toValue = "*";
else
toValue = stateNames.join(",");
m_transition.view()->executeInTransaction("TransitionForm::Set To", [this, toValue]() {
m_transition.variantProperty("to").setValue(toValue);
});
});
connect(ui->listWidgetFrom, &QListWidget::itemChanged, this, [this]() {
QTC_ASSERT(m_transition.isValid(), return );
const QmlItemNode root(m_transition.view()->rootModelNode());
QTC_ASSERT(root.isValid(), return );
const int stateCount = root.states().names().count();
QStringList stateNames;
for (const QListWidgetItem *item : ui->listWidgetFrom->findItems("*", Qt::MatchWildcard)) {
if (item->checkState() == Qt::Checked)
stateNames.append(item->text());
}
QString fromValue;
if (stateCount == stateNames.count())
fromValue = "*";
else
fromValue = stateNames.join(",");
m_transition.view()->executeInTransaction("TransitionForm::Set To", [this, fromValue]() {
m_transition.variantProperty("from").setValue(fromValue);
});
});
}
TransitionForm::~TransitionForm()
{
delete ui;
}
void TransitionForm::setTransition(const ModelNode &transition)
{
m_transition = transition;
if (m_transition.isValid()) {
ui->idLineEdit->setText(m_transition.displayName());
}
setupStatesLists();
}
ModelNode TransitionForm::transition() const
{
return m_transition;
}
void TransitionForm::setupStatesLists()
{
bool bTo = ui->listWidgetTo->blockSignals(true);
bool bFrom = ui->listWidgetFrom->blockSignals(true);
QAbstractItemModel *modelTo = ui->listWidgetTo->model();
modelTo->removeRows(0, modelTo->rowCount());
QAbstractItemModel *modelFrom = ui->listWidgetFrom->model();
modelFrom->removeRows(0, modelFrom->rowCount());
bool starFrom = true;
bool starTo = true;
QStringList fromList;
QStringList toList;
if (m_transition.hasVariantProperty("from")
&& m_transition.variantProperty("from").value().toString().trimmed() != "*") {
starFrom = false;
fromList = m_transition.variantProperty("from").value().toString().split(",");
}
if (m_transition.hasVariantProperty("to")
&& m_transition.variantProperty("to").value().toString().trimmed() != "*") {
starTo = false;
toList = m_transition.variantProperty("to").value().toString().split(",");
}
if (m_transition.isValid()) {
const QmlItemNode root(m_transition.view()->rootModelNode());
if (root.isValid()) {
const QmlModelStateGroup states = root.states();
for (const QString &stateName : states.names()) {
auto itemTo = new QListWidgetItem(stateName, ui->listWidgetTo);
ui->listWidgetTo->addItem(itemTo);
itemTo->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
if (starTo || toList.contains(stateName))
itemTo->setCheckState(Qt::Checked);
else
itemTo->setCheckState(Qt::Unchecked);
auto itemFrom = new QListWidgetItem(stateName, ui->listWidgetFrom);
ui->listWidgetFrom->addItem(itemFrom);
itemFrom->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
if (starFrom || fromList.contains(stateName))
itemFrom->setCheckState(Qt::Checked);
else
itemFrom->setCheckState(Qt::Unchecked);
}
}
}
ui->listWidgetTo->blockSignals(bTo);
ui->listWidgetFrom->blockSignals(bFrom);
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,57 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <qmltimeline.h>
#include <QWidget>
QT_FORWARD_DECLARE_CLASS(QSpinBox)
namespace QmlDesigner {
namespace Ui {
class TransitionForm;
}
class TransitionForm : public QWidget
{
Q_OBJECT
public:
explicit TransitionForm(QWidget *parent);
~TransitionForm() override;
void setTransition(const ModelNode &transition);
ModelNode transition() const;
private:
void setupStatesLists();
Ui::TransitionForm *ui;
ModelNode m_transition;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::TransitionForm</class>
<widget class="QWidget" name="QmlDesigner::TransitionForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>641</width>
<height>170</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Timeline Settings</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QListWidget" name="listWidgetTo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Transition ID:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="3" colspan="2">
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>49</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QListWidget" name="listWidgetFrom"/>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="idLineEdit">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>From</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>To</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -41,6 +41,7 @@
#include <formeditor/transitiontool.h> #include <formeditor/transitiontool.h>
#include <texttool/texttool.h> #include <texttool/texttool.h>
#include <timelineeditor/timelineview.h> #include <timelineeditor/timelineview.h>
#include <transitioneditor/transitioneditorview.h>
#include <pathtool/pathtool.h> #include <pathtool/pathtool.h>
#include <qmljseditor/qmljseditor.h> #include <qmljseditor/qmljseditor.h>
@@ -243,6 +244,10 @@ bool QmlDesignerPlugin::delayedInitialize()
timelineView->registerActions(); timelineView->registerActions();
} }
auto transitionEditorView = new QmlDesigner::TransitionEditorView;
d->viewManager.registerViewTakingOwnership(transitionEditorView);
transitionEditorView->registerActions();
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::SourceTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::SourceTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::ColorTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::ColorTool);
d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::AnnotationTool);

View File

@@ -32,6 +32,7 @@ include(components/curveeditor/curveeditor.pri)
include(components/bindingeditor/bindingeditor.pri) include(components/bindingeditor/bindingeditor.pri)
include(components/annotationeditor/annotationeditor.pri) include(components/annotationeditor/annotationeditor.pri)
include(components/richtexteditor/richtexteditor.pri) include(components/richtexteditor/richtexteditor.pri)
include(components/transitioneditor/transitioneditor.pri)
BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH)

View File

@@ -843,6 +843,26 @@ Project {
"timelineeditor/timelineview.h", "timelineeditor/timelineview.h",
"timelineeditor/timelinewidget.cpp", "timelineeditor/timelinewidget.cpp",
"timelineeditor/timelinewidget.h", "timelineeditor/timelinewidget.h",
"transitioneditor/transitioneditorview.cpp",
"transitioneditor/transitioneditorview.h",
"transitioneditor/transitioneditorwidget.cpp",
"transitioneditor/transitioneditorwidget.h",
"transitioneditor/transitioneditortoolbar.cpp",
"transitioneditor/transitioneditortoolbar.h",
"transitioneditor/transitioneditorgraphicsscene.cpp",
"transitioneditor/transitioneditorgraphicsscene.h",
"transitioneditor/transitioneditorgraphicslayout.cpp",
"transitioneditor/transitioneditorgraphicslayout.h",
"transitioneditor/transitioneditorsectionitem.cpp",
"transitioneditor/transitioneditorsectionitem.h",
"transitioneditor/transitioneditorpropertyitem.cpp",
"transitioneditor/transitioneditorpropertyitem.h",
"transitioneditor/transitioneditorsettingsdialog.cpp",
"transitioneditor/transitioneditorsettingsdialog.h",
"transitioneditor/transitioneditorsettingsdialog.ui"
"transitioneditor/transitionform.cpp",
"transitioneditor/transitionform.h",
"transitioneditor/transitioneditor.qrc"
] ]
} }