forked from qt-creator/qt-creator
QmlDesigner: Add animation playback for timeline
Adds animation playback, playback speed and looping inputs in the toolbar -Animation can be seen in the form editor in real time -Play button toggles the animation playback -Playback speed can be manually set -Looping can be toggled on and off -Looping range can be manually set Looping range can be created and edited in multiple ways. These shortcuts are currently placeholders and should be changed if needed. -LeftMouseButton+CTRL will set the loop start point and dragging the mouse will let you paint the range. -Hovering over the looping start or end point then dragging with LeftMouseButton+CTRL down will allow individual point movement -Pressing SHIFT will enable toolbar snapping when painting/moving looping points. will allow individual movement by dragging it with LeftMouseButton -Clicking a component in the timeline with LeftMouseButton+CTRL will set the components start and end time as the loop range. Using LeftMouseButton+CTRL+SHIFT instead will extend the current loop range to include the clicked component. Keyframes work the same way. -Using the selection tool by dragging the timeline with LeftMouseButton+CTRL will set the loop range to contain all the selected components. Using LeftMouseButton+CTRL+SHIFT instead will extend the current loop range to include the animations start, mid or end time, depending on which overlap with the selection tool rectangle. Keyframes work the same way. Task-number: QDS-1335 Change-Id: I8a623bae6ab43b41f894f2a33a1a597a48f82c68 Reviewed-by: Knud Dollereder <knud.dollereder@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -64,6 +64,7 @@ const char C_TO_START[] = "QmlDesigner.ToStart";
|
||||
const char C_TO_END[] = "QmlDesigner.ToEnd";
|
||||
const char C_PREVIOUS[] = "QmlDesigner.Previous";
|
||||
const char C_PLAY[] = "QmlDesigner.Play";
|
||||
const char C_LOOP_PLAYBACK[] = "QmlDesigner.LoopPlayback";
|
||||
const char C_NEXT[] = "QmlDesigner.Next";
|
||||
const char C_AUTO_KEYFRAME[] = "QmlDesigner.AutoKeyframe";
|
||||
const char C_CURVE_PICKER[] = "QmlDesigner.CurvePicker";
|
||||
|
@@ -598,6 +598,9 @@ void TimelineGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
void TimelineGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
|
||||
|
||||
m_layout->ruler()->updatePlaybackLoop(event);
|
||||
|
||||
m_tools.mouseMoveEvent(topItem, event);
|
||||
QGraphicsScene::mouseMoveEvent(event);
|
||||
}
|
||||
@@ -605,6 +608,10 @@ void TimelineGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
void TimelineGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
|
||||
|
||||
|
||||
m_layout->ruler()->updatePlaybackLoop(event);
|
||||
|
||||
/* The tool has handle the event last. */
|
||||
QGraphicsScene::mouseReleaseEvent(event);
|
||||
m_tools.mouseReleaseEvent(topItem, event);
|
||||
@@ -695,6 +702,11 @@ void TimelineGraphicsScene::invalidateSections()
|
||||
invalidateLayout();
|
||||
}
|
||||
|
||||
TimelineRulerSectionItem *TimelineGraphicsScene::layoutRuler() const
|
||||
{
|
||||
return m_layout->ruler();
|
||||
}
|
||||
|
||||
TimelineView *TimelineGraphicsScene::timelineView() const
|
||||
{
|
||||
return m_parent->timelineView();
|
||||
|
@@ -130,6 +130,7 @@ public:
|
||||
void setStartFrame(int frame);
|
||||
void setEndFrame(int frame);
|
||||
|
||||
TimelineRulerSectionItem *layoutRuler() const;
|
||||
TimelineView *timelineView() const;
|
||||
TimelineWidget *timelineWidget() const;
|
||||
TimelineToolBar *toolBar() const;
|
||||
|
@@ -131,6 +131,16 @@ TimelineKeyframeItem *TimelineMovableAbstractItem::asTimelineKeyframeItem(QGraph
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TimelineBarItem *TimelineMovableAbstractItem::asTimelineBarItem(QGraphicsItem *item)
|
||||
{
|
||||
auto movableItem = TimelineMovableAbstractItem::cast(item);
|
||||
|
||||
if (movableItem)
|
||||
return movableItem->asTimelineBarItem();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qreal TimelineMovableAbstractItem::rulerScaling() const
|
||||
{
|
||||
return qobject_cast<AbstractScrollGraphicsScene *>(scene())->rulerScaling();
|
||||
@@ -156,6 +166,12 @@ TimelineFrameHandle *TimelineMovableAbstractItem::asTimelineFrameHandle()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
TimelineBarItem *TimelineMovableAbstractItem::asTimelineBarItem()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TimelineMovableAbstractItem::isLocked() const
|
||||
{
|
||||
return false;
|
||||
|
@@ -35,6 +35,7 @@ namespace QmlDesigner {
|
||||
class AbstractScrollGraphicsScene;
|
||||
class TimelineKeyframeItem;
|
||||
class TimelineFrameHandle;
|
||||
class TimelineBarItem;
|
||||
|
||||
class TimelineMovableAbstractItem : public QGraphicsRectItem
|
||||
{
|
||||
@@ -51,6 +52,7 @@ public:
|
||||
static TimelineMovableAbstractItem *topMoveableItem(const QList<QGraphicsItem *> &items);
|
||||
static void emitScrollOffsetChanged(QGraphicsItem *item);
|
||||
static TimelineKeyframeItem *asTimelineKeyframeItem(QGraphicsItem *item);
|
||||
static TimelineBarItem *asTimelineBarItem(QGraphicsItem *item);
|
||||
|
||||
qreal rulerScaling() const;
|
||||
|
||||
@@ -68,6 +70,7 @@ public:
|
||||
|
||||
virtual TimelineKeyframeItem *asTimelineKeyframeItem();
|
||||
virtual TimelineFrameHandle *asTimelineFrameHandle();
|
||||
virtual TimelineBarItem *asTimelineBarItem();
|
||||
|
||||
virtual bool isLocked() const;
|
||||
|
||||
|
@@ -73,10 +73,25 @@ void TimelineMoveTool::mousePressEvent(TimelineMovableAbstractItem *item,
|
||||
if (currentItem() && currentItem()->isLocked())
|
||||
return;
|
||||
|
||||
TimelineGraphicsScene *graphicsScene = qobject_cast<TimelineGraphicsScene *>(scene());
|
||||
if (event->modifiers().testFlag(Qt::ControlModifier) && graphicsScene) { // TODO: Timeline bar animation is set as loop range. Select shortcut for this QDS-4941
|
||||
if (auto *current = currentItem()->asTimelineBarItem()) {
|
||||
qreal left = qRound(current->mapFromSceneToFrame(current->rect().left()));
|
||||
qreal right = qRound(current->mapFromSceneToFrame(current->rect().right()));
|
||||
const QList<qreal> positions = {left, right};
|
||||
graphicsScene->layoutRuler()->extendPlaybackLoop(positions, event->modifiers().testFlag(Qt::ShiftModifier));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *current = currentItem()->asTimelineKeyframeItem()) {
|
||||
const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x()));
|
||||
const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x()));
|
||||
m_pressKeyframeDelta = targetFrame - sourceFrame;
|
||||
|
||||
if (event->modifiers().testFlag(Qt::ControlModifier) && graphicsScene) {
|
||||
const QList<qreal> positions = {sourceFrame};
|
||||
graphicsScene->layoutRuler()->extendPlaybackLoop(positions, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -638,12 +638,23 @@ qreal TimelineRulerSectionItem::endFrame() const
|
||||
return m_end;
|
||||
}
|
||||
|
||||
qreal TimelineRulerSectionItem::playbackLoopStart() const
|
||||
{
|
||||
return m_playbackLoopStart > m_playbackLoopEnd ? m_playbackLoopEnd : m_playbackLoopStart;
|
||||
}
|
||||
|
||||
qreal TimelineRulerSectionItem::playbackLoopEnd() const
|
||||
{
|
||||
return m_playbackLoopEnd < m_playbackLoopStart ? m_playbackLoopStart : m_playbackLoopEnd;
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
|
||||
{
|
||||
static const QColor backgroundColor = Theme::getColor(Theme::DScontrolBackground);
|
||||
static const QColor penColor = Theme::getColor(Theme::PanelTextColorLight);
|
||||
static const QColor highlightColor = Theme::instance()->Theme::qmlDesignerButtonColor();
|
||||
static const QColor handleColor = Theme::getColor(Theme::QmlDesigner_HighlightColor);
|
||||
static const QColor playbackLoopColor = QColor(0, 255, 0, 255); // TODO: Timeline looping range color. Select color for this QDS-4941
|
||||
|
||||
const int scrollOffset = TimelineGraphicsScene::getScrollOffset(scene());
|
||||
|
||||
@@ -685,15 +696,22 @@ void TimelineRulerSectionItem::paint(QPainter *painter, const QStyleOptionGraphi
|
||||
painter->setPen(penColor);
|
||||
|
||||
const int height = size().height() - 1;
|
||||
|
||||
|
||||
|
||||
drawLine(painter,
|
||||
TimelineConstants::sectionWidth + scrollOffset
|
||||
- TimelineConstants::timelineLeftOffset,
|
||||
height,
|
||||
size().width() + scrollOffset,
|
||||
height);
|
||||
if (m_playbackLoopEnabled) {
|
||||
painter->setPen(playbackLoopColor);
|
||||
drawLine(painter,
|
||||
TimelineConstants::sectionWidth + (playbackLoopStart() * m_scaling),
|
||||
height,
|
||||
TimelineConstants::sectionWidth + (playbackLoopEnd() * m_scaling),
|
||||
height);
|
||||
}
|
||||
|
||||
painter->setPen(penColor);
|
||||
|
||||
QFont font = painter->font();
|
||||
font.setPixelSize(8);
|
||||
@@ -775,9 +793,112 @@ qreal TimelineRulerSectionItem::getFrameTick() const
|
||||
return m_frameTick;
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::setPlaybackLoopEnabled(bool value)
|
||||
{
|
||||
m_playbackLoopEnabled = value;
|
||||
if (m_playbackLoopStart == m_playbackLoopEnd) {
|
||||
m_playbackLoopStart = 0.;
|
||||
m_playbackLoopEnd = m_duration;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::setPlaybackLoopTimes(float startFrame, float endFrame)
|
||||
{
|
||||
if (m_playbackLoopEnabled) {
|
||||
startFrame = startFrame;
|
||||
endFrame = endFrame;
|
||||
m_playbackLoopStart = startFrame > m_duration ? m_duration : startFrame < 0.0 ? 0.0 : startFrame;
|
||||
m_playbackLoopEnd = endFrame > m_duration ? m_duration : endFrame < 0.0 ? 0.0 : endFrame;
|
||||
emit playbackLoopValuesChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::extendPlaybackLoop(const QList<qreal> &positions, bool reset)
|
||||
{
|
||||
if (m_playbackLoopEnabled) {
|
||||
qreal originalLeft, left = m_playbackLoopStart;
|
||||
qreal originalRight, right = m_playbackLoopEnd;
|
||||
|
||||
if (reset) {
|
||||
if (positions.count() >= 2) {
|
||||
left = m_duration;
|
||||
right = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto pos : positions) {
|
||||
qreal mapPos = pos;
|
||||
right = mapPos > right ? mapPos : right;
|
||||
left = mapPos < left ? mapPos : left;
|
||||
}
|
||||
|
||||
if (left != originalLeft && right != originalRight && (left != right))
|
||||
setPlaybackLoopTimes(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::updatePlaybackLoop(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (m_playbackLoopEnabled && event->modifiers().testFlag(Qt::ControlModifier)) { // Placeholder modifier
|
||||
QPointF pos = event->scenePos();
|
||||
TimelineGraphicsScene *graphicsScene = timelineScene();
|
||||
|
||||
qreal x = (qRound(pos.x()) - TimelineConstants::sectionWidth + graphicsScene->getScrollOffset(scene())
|
||||
- TimelineConstants::timelineLeftOffset) / m_scaling;
|
||||
x = x > m_duration ? m_duration : x;
|
||||
x = x < 0.0 ? 0.0 : x;
|
||||
if (event->modifiers().testFlag(Qt::ShiftModifier)) { // Placeholder modifier
|
||||
// snap to ruler markers
|
||||
x = graphicsScene->snap(x / m_scaling) * m_scaling;
|
||||
}
|
||||
|
||||
bool nearStart = abs(m_playbackLoopStart - x) < m_frameTick;
|
||||
bool nearEnd = abs(m_playbackLoopEnd - x) < m_frameTick;
|
||||
|
||||
int Type = event->type();
|
||||
if (Type == QEvent::GraphicsSceneMousePress) {
|
||||
if (nearStart) {
|
||||
m_isMovingPlaybackStart = true;
|
||||
} else {
|
||||
if (!nearEnd)
|
||||
m_playbackLoopStart = x;
|
||||
m_playbackLoopEnd = x;
|
||||
m_isPaintingPlaybackLoopRange = true;
|
||||
}
|
||||
emit playbackLoopValuesChanged();
|
||||
update();
|
||||
} else if (Type == QEvent::GraphicsSceneMouseMove) {
|
||||
if (!m_isPaintingPlaybackLoopRange && (nearStart || nearEnd)) {
|
||||
if (cursor().shape() != Qt::SizeHorCursor)
|
||||
setCursor(QCursor(Qt::SizeHorCursor));
|
||||
} else if (cursor().shape() != Qt::ArrowCursor) {
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
}
|
||||
if (m_isMovingPlaybackStart) {
|
||||
m_playbackLoopStart = x;
|
||||
emit playbackLoopValuesChanged();
|
||||
update();
|
||||
} else if (m_isPaintingPlaybackLoopRange){
|
||||
m_playbackLoopEnd = x;
|
||||
emit playbackLoopValuesChanged();
|
||||
update();
|
||||
}
|
||||
} else if (Type == QEvent::GraphicsSceneMouseRelease) {
|
||||
m_isPaintingPlaybackLoopRange = m_isMovingPlaybackStart = false;
|
||||
}
|
||||
} else if (cursor().shape() != Qt::ArrowCursor) {
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRulerSectionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
TimelineItem::mousePressEvent(event);
|
||||
updatePlaybackLoop(event);
|
||||
emit rulerClicked(event->pos());
|
||||
}
|
||||
|
||||
@@ -869,6 +990,11 @@ bool TimelineBarItem::isLocked() const
|
||||
return sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked();
|
||||
}
|
||||
|
||||
TimelineBarItem *TimelineBarItem::asTimelineBarItem()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void TimelineBarItem::scrollOffsetChanged()
|
||||
{
|
||||
sectionItem()->invalidateBar();
|
||||
|
@@ -52,6 +52,7 @@ public:
|
||||
|
||||
bool isLocked() const override;
|
||||
|
||||
TimelineBarItem *asTimelineBarItem() override;
|
||||
protected:
|
||||
void scrollOffsetChanged() override;
|
||||
void paint(QPainter *painter,
|
||||
@@ -148,7 +149,7 @@ class TimelineRulerSectionItem : public TimelineItem
|
||||
|
||||
signals:
|
||||
void rulerClicked(const QPointF &pos);
|
||||
|
||||
void playbackLoopValuesChanged();
|
||||
void zoomChanged(int zoom);
|
||||
|
||||
public:
|
||||
@@ -167,10 +168,17 @@ public:
|
||||
qreal durationViewportLength() const;
|
||||
qreal startFrame() const;
|
||||
qreal endFrame() const;
|
||||
qreal playbackLoopStart() const;
|
||||
qreal playbackLoopEnd() const;
|
||||
|
||||
QComboBox *comboBox() const;
|
||||
|
||||
void setSizeHints(int width);
|
||||
void setPlaybackLoopEnabled(bool value);
|
||||
void setPlaybackLoopTimes(float start, float end);
|
||||
void extendPlaybackLoop(const QList<qreal> &positions, bool reset);
|
||||
void updatePlaybackLoop(QGraphicsSceneMouseEvent *event);
|
||||
|
||||
|
||||
signals:
|
||||
void addTimelineClicked();
|
||||
@@ -194,6 +202,11 @@ private:
|
||||
qreal m_end = 0;
|
||||
qreal m_scaling = 1;
|
||||
qreal m_frameTick = 1.; // distance between 2 tick steps (in frames) on the ruler at current scale
|
||||
qreal m_playbackLoopStart = 0.;
|
||||
qreal m_playbackLoopEnd = 0.;
|
||||
bool m_playbackLoopEnabled = false;
|
||||
bool m_isPaintingPlaybackLoopRange = false;
|
||||
bool m_isMovingPlaybackStart = false;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -162,6 +162,7 @@ void TimelineSelectionTool::resetHighlights()
|
||||
void TimelineSelectionTool::aboutToSelect(SelectionMode mode, QList<QGraphicsItem *> items)
|
||||
{
|
||||
resetHighlights();
|
||||
m_playbackLoopTimeSteps.clear();
|
||||
|
||||
for (auto *item : items) {
|
||||
if (auto *keyframe = TimelineMovableAbstractItem::asTimelineKeyframeItem(item)) {
|
||||
@@ -179,15 +180,38 @@ void TimelineSelectionTool::aboutToSelect(SelectionMode mode, QList<QGraphicsIte
|
||||
else
|
||||
keyframe->setHighlighted(true);
|
||||
|
||||
if (mode == SelectionMode::Toggle || mode == SelectionMode::Add) // TODO: Timeline keyframe highlight with selection tool is set as or added to loop range. Select shortcut for this QDS-4941
|
||||
m_playbackLoopTimeSteps << keyframe->mapFromSceneToFrame((keyframe->rect().center() + item->scenePos()).x());
|
||||
|
||||
m_aboutToSelectBuffer << keyframe;
|
||||
} else if (auto *barItem = TimelineMovableAbstractItem::asTimelineBarItem(item)) {
|
||||
QRectF rect = barItem->rect();
|
||||
QPointF center = rect.center() + item->scenePos();
|
||||
QPointF left = QPointF(rect.left(), 0) + item->scenePos();
|
||||
QPointF right = QPointF(rect.right(), 0) + item->scenePos();
|
||||
if (mode == SelectionMode::Add) { // TODO: Timeline bar item highlight with selection tool is added to loop range. Select shortcut for this QDS-4941
|
||||
if (m_selectionRect->rect().contains(left))
|
||||
m_playbackLoopTimeSteps << barItem->mapFromSceneToFrame(left.x());
|
||||
if (m_selectionRect->rect().contains(right))
|
||||
m_playbackLoopTimeSteps << barItem->mapFromSceneToFrame(right.x());
|
||||
if (m_selectionRect->rect().contains(center))
|
||||
m_playbackLoopTimeSteps << barItem->mapFromSceneToFrame(center.x());
|
||||
} else if (mode == SelectionMode::Toggle && m_selectionRect->rect().contains(center)) { // TODO: Timeline bar item highlight with selection tool is set as loop range. Select shortcut for this QDS-4941
|
||||
m_playbackLoopTimeSteps << barItem->mapFromSceneToFrame(left.x());
|
||||
m_playbackLoopTimeSteps << barItem->mapFromSceneToFrame(right.x());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineSelectionTool::commitSelection(SelectionMode mode)
|
||||
{
|
||||
if (m_playbackLoopTimeSteps.count())
|
||||
qobject_cast<TimelineGraphicsScene *>(scene())->layoutRuler()->extendPlaybackLoop(m_playbackLoopTimeSteps,
|
||||
mode == SelectionMode::Toggle); // TODO: Highlighting items with selection tool is set or added to loop range. Select shortcut for this QDS-4941
|
||||
scene()->selectKeyframes(mode, m_aboutToSelectBuffer);
|
||||
m_aboutToSelectBuffer.clear();
|
||||
m_playbackLoopTimeSteps.clear();
|
||||
}
|
||||
|
||||
} // End namespace QmlDesigner.
|
||||
|
@@ -37,7 +37,7 @@ namespace QmlDesigner {
|
||||
class TimelineToolDelegate;
|
||||
|
||||
class TimelineKeyframeItem;
|
||||
|
||||
class TimelineBarItem;
|
||||
class TimelineGraphicsScene;
|
||||
|
||||
enum class SelectionMode { New, Add, Remove, Toggle };
|
||||
@@ -81,6 +81,7 @@ private:
|
||||
QGraphicsRectItem *m_selectionRect;
|
||||
|
||||
QList<TimelineKeyframeItem *> m_aboutToSelectBuffer;
|
||||
QList<qreal> m_playbackLoopTimeSteps;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -56,6 +56,20 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class LineEditDoubleValidator : public QDoubleValidator
|
||||
{
|
||||
public:
|
||||
LineEditDoubleValidator(double bottom, double top, int decimals, QLineEdit *parent)
|
||||
: QDoubleValidator(bottom, top, decimals, parent),
|
||||
m_value(1.0) { parent->setText(locale().toString(1.0, 'f', 1)); }
|
||||
~LineEditDoubleValidator() {};
|
||||
|
||||
void fixup(QString &input) const override { input = locale().toString(m_value, 'f', 1); };
|
||||
void setValue(double value) { m_value = value; };
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
static bool isSpacer(QObject *object)
|
||||
{
|
||||
return object->property("spacer_widget").toBool();
|
||||
@@ -189,6 +203,11 @@ void TimelineToolBar::setScaleFactor(int factor)
|
||||
m_scale->setValue(factor);
|
||||
}
|
||||
|
||||
void TimelineToolBar::setPlayState(bool state)
|
||||
{
|
||||
m_playing->setChecked(state);
|
||||
}
|
||||
|
||||
void TimelineToolBar::setActionEnabled(const QString &name, bool enabled)
|
||||
{
|
||||
for (auto *action : actions())
|
||||
@@ -277,14 +296,15 @@ void TimelineToolBar::createCenterControls()
|
||||
addAction(previous);
|
||||
|
||||
addSpacing(2);
|
||||
|
||||
auto *play = createAction(TimelineConstants::C_PLAY,
|
||||
TimelineIcons::START_PLAYBACK.icon(),
|
||||
QIcon playbackIcon = TimelineUtils::mergeIcons(TimelineIcons::PAUSE_PLAYBACK,
|
||||
TimelineIcons::START_PLAYBACK);
|
||||
m_playing = createAction(TimelineConstants::C_PLAY,
|
||||
playbackIcon,
|
||||
tr("Play"),
|
||||
QKeySequence(Qt::Key_Space));
|
||||
|
||||
connect(play, &QAction::triggered, this, &TimelineToolBar::playTriggered);
|
||||
addAction(play);
|
||||
m_playing->setCheckable(true);
|
||||
connect(m_playing, &QAction::triggered, this, &TimelineToolBar::playTriggered);
|
||||
addAction(m_playing);
|
||||
|
||||
addSpacing(2);
|
||||
|
||||
@@ -306,13 +326,40 @@ void TimelineToolBar::createCenterControls()
|
||||
connect(toEnd, &QAction::triggered, this, &TimelineToolBar::toLastFrameTriggered);
|
||||
addAction(toEnd);
|
||||
|
||||
#if 0
|
||||
auto *loop = new QAction(TimelineIcons::LOOP_PLAYBACK.icon(), tr("Loop"), this);
|
||||
addAction(loop);
|
||||
#endif
|
||||
addSpacing(10);
|
||||
|
||||
addSeparator();
|
||||
|
||||
addSpacing(10);
|
||||
|
||||
auto *loopAnimation = createAction(TimelineConstants::C_LOOP_PLAYBACK,
|
||||
TimelineIcons::LOOP_PLAYBACK.icon(),
|
||||
tr("Loop Playback"),
|
||||
QKeySequence((Qt::ControlModifier | Qt::ShiftModifier) + Qt::Key_Space)); // TODO: Toggles looping. Select shortcut for this QDS-4941
|
||||
|
||||
loopAnimation->setCheckable(true);
|
||||
connect(loopAnimation, &QAction::toggled, [&](bool value) { emit loopPlaybackToggled(value);} );
|
||||
|
||||
addAction(loopAnimation);
|
||||
|
||||
addSpacing(5);
|
||||
|
||||
m_animationPlaybackSpeed = createToolBarLineEdit(this);
|
||||
LineEditDoubleValidator *validator = new LineEditDoubleValidator(0.1, 100.0, 1, m_animationPlaybackSpeed);
|
||||
m_animationPlaybackSpeed->setValidator(validator);
|
||||
m_animationPlaybackSpeed->setToolTip(tr("Playback Speed"));
|
||||
addWidget(m_animationPlaybackSpeed);
|
||||
|
||||
auto emitPlaybackSpeedChanged = [this, validator]() {
|
||||
bool ok = false;
|
||||
if (double res = validator->locale().toDouble(m_animationPlaybackSpeed->text(), &ok); ok==true) {
|
||||
validator->setValue(res);
|
||||
m_animationPlaybackSpeed->setText(locale().toString(res, 'f', 1));
|
||||
emit playbackSpeedChanged(res);
|
||||
}
|
||||
};
|
||||
connect(m_animationPlaybackSpeed, &QLineEdit::editingFinished, emitPlaybackSpeedChanged);
|
||||
|
||||
addSeparator();
|
||||
|
||||
m_currentFrame = createToolBarLineEdit(this);
|
||||
|
@@ -58,6 +58,7 @@ signals:
|
||||
|
||||
void recordToggled(bool val);
|
||||
void loopPlaybackToggled(bool val);
|
||||
void playbackSpeedChanged(float val);
|
||||
|
||||
void scaleFactorChanged(int value);
|
||||
void startFrameChanged(int value);
|
||||
@@ -80,6 +81,7 @@ public:
|
||||
void setCurrentFrame(qreal frame);
|
||||
void setEndFrame(qreal frame);
|
||||
void setScaleFactor(int factor);
|
||||
void setPlayState(bool state);
|
||||
|
||||
void setActionEnabled(const QString &name, bool enabled);
|
||||
void removeTimeline(const QmlTimeline &timeline);
|
||||
@@ -102,7 +104,9 @@ private:
|
||||
QLineEdit *m_firstFrame = nullptr;
|
||||
QLineEdit *m_currentFrame = nullptr;
|
||||
QLineEdit *m_lastFrame = nullptr;
|
||||
QLineEdit *m_animationPlaybackSpeed = nullptr;
|
||||
|
||||
QAction *m_playing = nullptr;
|
||||
QAction *m_recording = nullptr;
|
||||
bool m_blockReflection = false;
|
||||
};
|
||||
|
@@ -60,6 +60,7 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QtGlobal>
|
||||
#include <QSpacerItem>
|
||||
#include <QVariantAnimation>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -123,6 +124,9 @@ TimelineWidget::TimelineWidget(TimelineView *view)
|
||||
, m_graphicsScene(new TimelineGraphicsScene(this))
|
||||
, m_addButton(new QPushButton(this))
|
||||
, m_onboardingContainer(new QWidget(this))
|
||||
, m_loopPlayback(false)
|
||||
, m_playbackSpeed(1)
|
||||
, m_playbackAnimation(new QVariantAnimation(this))
|
||||
{
|
||||
setWindowTitle(tr("Timeline", "Title of timeline view"));
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
@@ -277,6 +281,23 @@ TimelineWidget::TimelineWidget(TimelineView *view)
|
||||
m_graphicsScene->setZoom(std::clamp(m_graphicsScene->zoom() + s, 0, 100), ps);
|
||||
});
|
||||
installEventFilter(filter);
|
||||
|
||||
m_playbackAnimation->stop();
|
||||
auto playAnimation = [this](QVariant frame) { graphicsScene()->setCurrentFrame(qRound(frame.toDouble())); };
|
||||
connect(m_playbackAnimation, &QVariantAnimation::valueChanged, playAnimation);
|
||||
|
||||
auto updatePlaybackLoopValues = [this]() {
|
||||
updatePlaybackValues();
|
||||
};
|
||||
connect(graphicsScene()->layoutRuler(), &TimelineRulerSectionItem::playbackLoopValuesChanged, updatePlaybackLoopValues);
|
||||
|
||||
auto setPlaybackState = [this](QAbstractAnimation::State newState, QAbstractAnimation::State oldState) {
|
||||
m_toolbar->setPlayState(newState == QAbstractAnimation::State::Running);
|
||||
};
|
||||
connect(m_playbackAnimation, &QVariantAnimation::stateChanged, setPlaybackState);
|
||||
|
||||
auto onFinish = [this]() { graphicsScene()->setCurrentFrame(m_playbackAnimation->startValue().toInt()); };
|
||||
connect(m_playbackAnimation, &QVariantAnimation::finished, onFinish);
|
||||
}
|
||||
|
||||
void TimelineWidget::connectToolbar()
|
||||
@@ -320,6 +341,21 @@ void TimelineWidget::connectToolbar()
|
||||
|
||||
connect(m_toolbar, &TimelineToolBar::recordToggled, this, &TimelineWidget::setTimelineRecording);
|
||||
|
||||
connect(m_toolbar, &TimelineToolBar::playTriggered, this, &TimelineWidget::toggleAnimationPlayback);
|
||||
|
||||
auto setLoopAnimation = [this](bool loop) {
|
||||
graphicsScene()->layoutRuler()->setPlaybackLoopEnabled(loop);
|
||||
if (m_playbackAnimation->state() == QAbstractAnimation::Running)
|
||||
m_playbackAnimation->pause();
|
||||
m_loopPlayback = loop;
|
||||
};
|
||||
connect(m_toolbar, &TimelineToolBar::loopPlaybackToggled, setLoopAnimation);
|
||||
auto setPlaybackSpeed = [this](float val) {
|
||||
m_playbackSpeed = val;
|
||||
updatePlaybackValues();
|
||||
};
|
||||
connect(m_toolbar, &TimelineToolBar::playbackSpeedChanged, setPlaybackSpeed);
|
||||
|
||||
connect(m_toolbar,
|
||||
&TimelineToolBar::openEasingCurveEditor,
|
||||
this,
|
||||
@@ -329,19 +365,6 @@ void TimelineWidget::connectToolbar()
|
||||
&TimelineToolBar::settingDialogClicked,
|
||||
m_timelineView,
|
||||
&TimelineView::openSettingsDialog);
|
||||
|
||||
for (auto action : QmlDesignerPlugin::instance()->designerActionManager().designerActions()) {
|
||||
if (action->menuId() == "LivePreview") {
|
||||
QObject::connect(m_toolbar,
|
||||
&TimelineToolBar::playTriggered,
|
||||
action->action(),
|
||||
[action]() {
|
||||
action->action()->setChecked(false);
|
||||
action->action()->triggered(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setTimelineActive(false);
|
||||
}
|
||||
|
||||
@@ -383,6 +406,55 @@ void TimelineWidget::openEasingCurveEditor()
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::updatePlaybackValues()
|
||||
{
|
||||
QmlTimeline currentTimeline = graphicsScene()->currentTimeline();
|
||||
qreal endFrame = currentTimeline.endKeyframe();
|
||||
qreal startFrame = currentTimeline.startKeyframe();
|
||||
qreal duration = currentTimeline.duration();
|
||||
|
||||
if (m_loopPlayback) {
|
||||
m_playbackAnimation->setLoopCount(-1);
|
||||
qreal loopStart = graphicsScene()->layoutRuler()->playbackLoopStart();
|
||||
qreal loopEnd = graphicsScene()->layoutRuler()->playbackLoopEnd();
|
||||
startFrame = qRound(startFrame + loopStart);
|
||||
endFrame = qRound(startFrame + (loopEnd - loopStart));
|
||||
duration = endFrame - startFrame;
|
||||
} else {
|
||||
m_playbackAnimation->setLoopCount(1);
|
||||
}
|
||||
|
||||
if (duration > 0.0f) {
|
||||
qreal animationDuration = duration * (1.0 / m_playbackSpeed);
|
||||
m_playbackAnimation->setDuration(animationDuration);
|
||||
} else {
|
||||
if (m_playbackAnimation->state() == QAbstractAnimation::Running)
|
||||
m_playbackAnimation->stop();
|
||||
}
|
||||
qreal a = m_playbackAnimation->duration();
|
||||
qreal newCurrentTime = (currentTimeline.currentKeyframe() - startFrame) * (1.0 / m_playbackSpeed);
|
||||
if (qRound(m_playbackAnimation->startValue().toDouble()) != qRound(startFrame)
|
||||
|| qRound(m_playbackAnimation->endValue().toDouble()) != qRound(endFrame)) {
|
||||
newCurrentTime = 0;
|
||||
}
|
||||
m_playbackAnimation->setStartValue(qRound(startFrame));
|
||||
m_playbackAnimation->setEndValue(qRound(endFrame));
|
||||
m_playbackAnimation->setCurrentTime(newCurrentTime);
|
||||
}
|
||||
|
||||
void TimelineWidget::toggleAnimationPlayback()
|
||||
{
|
||||
QmlTimeline currentTimeline = graphicsScene()->currentTimeline();
|
||||
if (currentTimeline.isValid() && m_playbackSpeed > 0.0) {
|
||||
if (m_playbackAnimation->state() == QAbstractAnimation::Running) {
|
||||
m_playbackAnimation->pause();
|
||||
} else {
|
||||
updatePlaybackValues();
|
||||
m_playbackAnimation->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::setTimelineRecording(bool value)
|
||||
{
|
||||
ModelNode node = timelineView()->modelNodeForId(m_toolbar->currentTimelineId());
|
||||
|
@@ -39,6 +39,7 @@ QT_FORWARD_DECLARE_CLASS(QResizeEvent)
|
||||
QT_FORWARD_DECLARE_CLASS(QShowEvent)
|
||||
QT_FORWARD_DECLARE_CLASS(QString)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
QT_FORWARD_DECLARE_CLASS(QVariantAnimation)
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -74,9 +75,11 @@ public:
|
||||
public slots:
|
||||
void selectionChanged();
|
||||
void openEasingCurveEditor();
|
||||
void toggleAnimationPlayback();
|
||||
void setTimelineRecording(bool value);
|
||||
void changeScaleFactor(int factor);
|
||||
void scroll(const TimelineUtils::Side &side);
|
||||
void updatePlaybackValues();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
@@ -105,6 +108,10 @@ private:
|
||||
QPushButton *m_addButton = nullptr;
|
||||
|
||||
QWidget *m_onboardingContainer = nullptr;
|
||||
|
||||
bool m_loopPlayback;
|
||||
qreal m_playbackSpeed;
|
||||
QVariantAnimation *m_playbackAnimation;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -425,6 +425,11 @@ void TransitionEditorGraphicsScene::invalidateSections()
|
||||
invalidateLayout();
|
||||
}
|
||||
|
||||
TimelineRulerSectionItem *TransitionEditorGraphicsScene::layoutRuler() const
|
||||
{
|
||||
return m_layout->ruler();
|
||||
}
|
||||
|
||||
TransitionEditorView *TransitionEditorGraphicsScene::transitionEditorView() const
|
||||
{
|
||||
return m_parent->transitionEditorView();
|
||||
|
@@ -72,6 +72,7 @@ public:
|
||||
void invalidateLayout();
|
||||
void setDuration(int duration);
|
||||
|
||||
TimelineRulerSectionItem *layoutRuler() const;
|
||||
TransitionEditorView *transitionEditorView() const;
|
||||
TransitionEditorWidget *transitionEditorWidget() const;
|
||||
TransitionEditorToolBar *toolBar() const;
|
||||
|
Reference in New Issue
Block a user