forked from qt-creator/qt-creator
Connect CurveEditor edits to the timeline module
Change-Id: Ic00e0840da34bdbb8627b2fe2d8546a867b24966 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -24,40 +24,69 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "animationcurve.h"
|
#include "animationcurve.h"
|
||||||
#include "detail/curvesegment.h"
|
#include "curvesegment.h"
|
||||||
|
#include "detail/utils.h"
|
||||||
|
|
||||||
|
#include <QEasingCurve>
|
||||||
#include <QLineF>
|
#include <QLineF>
|
||||||
|
#include <QPainterPath>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
AnimationCurve::AnimationCurve()
|
AnimationCurve::AnimationCurve()
|
||||||
: m_frames()
|
: m_fromData(false)
|
||||||
|
, m_minY(std::numeric_limits<double>::max())
|
||||||
|
, m_maxY(std::numeric_limits<double>::lowest())
|
||||||
|
, m_frames()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
|
AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
|
||||||
: m_frames(frames)
|
: m_fromData(false)
|
||||||
, m_minY(std::numeric_limits<double>::max())
|
, m_minY(std::numeric_limits<double>::max())
|
||||||
, m_maxY(std::numeric_limits<double>::lowest())
|
, m_maxY(std::numeric_limits<double>::lowest())
|
||||||
|
, m_frames(frames)
|
||||||
{
|
{
|
||||||
if (isValid()) {
|
analyze();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto e : extrema()) {
|
AnimationCurve::AnimationCurve(const QEasingCurve &easing, const QPointF &start, const QPointF &end)
|
||||||
|
: m_fromData(true)
|
||||||
|
, m_minY(std::numeric_limits<double>::max())
|
||||||
|
, m_maxY(std::numeric_limits<double>::lowest())
|
||||||
|
, m_frames()
|
||||||
|
{
|
||||||
|
auto mapPosition = [start, end](const QPointF &pos) {
|
||||||
|
QPointF slope(end.x() - start.x(), end.y() - start.y());
|
||||||
|
return QPointF(start.x() + slope.x() * pos.x(), start.y() + slope.y() * pos.y());
|
||||||
|
};
|
||||||
|
|
||||||
if (m_minY > e.y())
|
QVector<QPointF> points = easing.toCubicSpline();
|
||||||
m_minY = e.y();
|
int numSegments = points.count() / 3;
|
||||||
|
|
||||||
if (m_maxY < e.y())
|
Keyframe current;
|
||||||
m_maxY = e.y();
|
Keyframe tmp(start);
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &frame : qAsConst(m_frames)) {
|
current.setInterpolation(Keyframe::Interpolation::Bezier);
|
||||||
if (frame.position().y() < m_minY)
|
tmp.setInterpolation(Keyframe::Interpolation::Bezier);
|
||||||
m_minY = frame.position().y();
|
|
||||||
|
|
||||||
if (frame.position().y() > m_maxY)
|
for (int i = 0; i < numSegments; i++) {
|
||||||
m_maxY = frame.position().y();
|
QPointF p1 = mapPosition(points.at(i * 3));
|
||||||
}
|
QPointF p2 = mapPosition(points.at(i * 3 + 1));
|
||||||
|
QPointF p3 = mapPosition(points.at(i * 3 + 2));
|
||||||
|
|
||||||
|
current.setPosition(tmp.position());
|
||||||
|
current.setLeftHandle(tmp.leftHandle());
|
||||||
|
current.setRightHandle(p1);
|
||||||
|
|
||||||
|
m_frames.push_back(current);
|
||||||
|
|
||||||
|
tmp.setLeftHandle(p2);
|
||||||
|
tmp.setPosition(p3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_frames.push_back(tmp);
|
||||||
|
|
||||||
|
analyze();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationCurve::isValid() const
|
bool AnimationCurve::isValid() const
|
||||||
@@ -65,6 +94,11 @@ bool AnimationCurve::isValid() const
|
|||||||
return m_frames.size() >= 2;
|
return m_frames.size() >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnimationCurve::isFromData() const
|
||||||
|
{
|
||||||
|
return m_fromData;
|
||||||
|
}
|
||||||
|
|
||||||
double AnimationCurve::minimumTime() const
|
double AnimationCurve::minimumTime() const
|
||||||
{
|
{
|
||||||
if (!m_frames.empty())
|
if (!m_frames.empty())
|
||||||
@@ -91,6 +125,70 @@ double AnimationCurve::maximumValue() const
|
|||||||
return m_maxY;
|
return m_maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurveSegment AnimationCurve::segment(double time) const
|
||||||
|
{
|
||||||
|
CurveSegment seg;
|
||||||
|
for (auto &frame : m_frames) {
|
||||||
|
if (frame.position().x() > time) {
|
||||||
|
seg.setRight(frame);
|
||||||
|
return seg;
|
||||||
|
}
|
||||||
|
seg.setLeft(frame);
|
||||||
|
}
|
||||||
|
return CurveSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CurveSegment> AnimationCurve::segments() const
|
||||||
|
{
|
||||||
|
if (m_frames.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<CurveSegment> out;
|
||||||
|
|
||||||
|
CurveSegment current;
|
||||||
|
current.setLeft(m_frames.at(0));
|
||||||
|
|
||||||
|
for (size_t i = 1; i < m_frames.size(); ++i) {
|
||||||
|
current.setRight(m_frames[i]);
|
||||||
|
out.push_back(current);
|
||||||
|
current.setLeft(m_frames[i]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF mapEasing(const QPointF &start, const QPointF &end, const QPointF &pos)
|
||||||
|
{
|
||||||
|
QPointF slope(end.x() - start.x(), end.y() - start.y());
|
||||||
|
return QPointF(start.x() + slope.x() * pos.x(), start.y() + slope.y() * pos.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath AnimationCurve::simplePath() const
|
||||||
|
{
|
||||||
|
if (m_frames.empty())
|
||||||
|
return QPainterPath();
|
||||||
|
|
||||||
|
QPainterPath path(m_frames.front().position());
|
||||||
|
|
||||||
|
CurveSegment segment;
|
||||||
|
segment.setLeft(m_frames.front());
|
||||||
|
|
||||||
|
for (size_t i = 1; i < m_frames.size(); ++i) {
|
||||||
|
segment.setRight(m_frames[i]);
|
||||||
|
segment.extend(path);
|
||||||
|
segment.setLeft(m_frames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath AnimationCurve::intersectionPath() const
|
||||||
|
{
|
||||||
|
QPainterPath path = simplePath();
|
||||||
|
QPainterPath reversed = path.toReversed();
|
||||||
|
path.connectPath(reversed);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Keyframe> AnimationCurve::keyframes() const
|
std::vector<Keyframe> AnimationCurve::keyframes() const
|
||||||
{
|
{
|
||||||
return m_frames;
|
return m_frames;
|
||||||
@@ -100,19 +198,10 @@ std::vector<QPointF> AnimationCurve::extrema() const
|
|||||||
{
|
{
|
||||||
std::vector<QPointF> out;
|
std::vector<QPointF> out;
|
||||||
|
|
||||||
CurveSegment segment;
|
for (auto &&segment : segments()) {
|
||||||
segment.setLeft(m_frames.at(0));
|
|
||||||
|
|
||||||
for (size_t i = 1; i < m_frames.size(); ++i) {
|
|
||||||
|
|
||||||
segment.setRight(m_frames[i]);
|
|
||||||
|
|
||||||
const auto es = segment.extrema();
|
const auto es = segment.extrema();
|
||||||
out.insert(std::end(out), std::begin(es), std::end(es));
|
out.insert(std::end(out), std::begin(es), std::end(es));
|
||||||
|
|
||||||
segment.setLeft(m_frames[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +230,7 @@ std::vector<double> AnimationCurve::xForY(double y, uint segment) const
|
|||||||
return std::vector<double>();
|
return std::vector<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationCurve::intersects(const QPointF &coord, double radius)
|
bool AnimationCurve::intersects(const QPointF &coord, double radiusX, double radiusY) const
|
||||||
{
|
{
|
||||||
if (m_frames.size() < 2)
|
if (m_frames.size() < 2)
|
||||||
return false;
|
return false;
|
||||||
@@ -152,36 +241,94 @@ bool AnimationCurve::intersects(const QPointF &coord, double radius)
|
|||||||
current.setLeft(m_frames.at(0));
|
current.setLeft(m_frames.at(0));
|
||||||
|
|
||||||
for (size_t i = 1; i < m_frames.size(); ++i) {
|
for (size_t i = 1; i < m_frames.size(); ++i) {
|
||||||
Keyframe &frame = m_frames.at(i);
|
const Keyframe &frame = m_frames.at(i);
|
||||||
|
|
||||||
current.setRight(frame);
|
current.setRight(frame);
|
||||||
|
|
||||||
if (current.containsX(coord.x() - radius) ||
|
if (current.containsX(coord.x() - radiusX) || current.containsX(coord.x())
|
||||||
current.containsX(coord.x()) ||
|
|| current.containsX(coord.x() + radiusX)) {
|
||||||
current.containsX(coord.x() + radius)) {
|
|
||||||
influencer.push_back(current);
|
influencer.push_back(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.position().x() > coord.x() + radius)
|
if (frame.position().x() > coord.x() + radiusX)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
current.setLeft(frame);
|
current.setLeft(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &segment : influencer) {
|
for (auto &segment : influencer) {
|
||||||
for (auto &y : segment.yForX(coord.x())) {
|
if (segment.intersects(coord, radiusX, radiusY))
|
||||||
QLineF line(coord.x(), y, coord.x(), coord.y());
|
return true;
|
||||||
if (line.length() < radius)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &x : segment.xForY(coord.y())) {
|
|
||||||
QLineF line(x, coord.y(), coord.x(), coord.y());
|
|
||||||
if (line.length() < radius)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationCurve::append(const AnimationCurve &other)
|
||||||
|
{
|
||||||
|
if (!other.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!isValid()) {
|
||||||
|
m_frames = other.keyframes();
|
||||||
|
analyze();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Keyframe> otherFrames = other.keyframes();
|
||||||
|
m_frames.back().setRightHandle(otherFrames.front().rightHandle());
|
||||||
|
m_frames.insert(std::end(m_frames), std::begin(otherFrames) + 1, std::end(otherFrames));
|
||||||
|
analyze();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurve::insert(double time)
|
||||||
|
{
|
||||||
|
CurveSegment seg = segment(time);
|
||||||
|
|
||||||
|
if (!seg.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto insertFrames = [this](std::array<Keyframe, 3> &&frames) {
|
||||||
|
auto samePosition = [frames](const Keyframe &frame) {
|
||||||
|
return frame.position() == frames[0].position();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto iter = std::find_if(m_frames.begin(), m_frames.end(), samePosition);
|
||||||
|
if (iter != m_frames.end()) {
|
||||||
|
auto erased = m_frames.erase(iter, iter + 2);
|
||||||
|
m_frames.insert(erased, frames.begin(), frames.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
insertFrames(seg.splitAt(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurve::analyze()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
m_minY = std::numeric_limits<double>::max();
|
||||||
|
m_maxY = std::numeric_limits<double>::lowest();
|
||||||
|
|
||||||
|
auto byTime = [](const auto &a, const auto &b) {
|
||||||
|
return a.position().x() < b.position().x();
|
||||||
|
};
|
||||||
|
std::sort(m_frames.begin(), m_frames.end(), byTime);
|
||||||
|
|
||||||
|
for (auto e : extrema()) {
|
||||||
|
if (m_minY > e.y())
|
||||||
|
m_minY = e.y();
|
||||||
|
|
||||||
|
if (m_maxY < e.y())
|
||||||
|
m_maxY = e.y();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &frame : qAsConst(m_frames)) {
|
||||||
|
if (frame.position().y() < m_minY)
|
||||||
|
m_minY = frame.position().y();
|
||||||
|
|
||||||
|
if (frame.position().y() > m_maxY)
|
||||||
|
m_maxY = frame.position().y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -27,10 +27,16 @@
|
|||||||
|
|
||||||
#include "keyframe.h"
|
#include "keyframe.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QEasingCurve);
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QPainterPath);
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
|
class CurveSegment;
|
||||||
|
|
||||||
class AnimationCurve
|
class AnimationCurve
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -38,8 +44,12 @@ public:
|
|||||||
|
|
||||||
AnimationCurve(const std::vector<Keyframe> &frames);
|
AnimationCurve(const std::vector<Keyframe> &frames);
|
||||||
|
|
||||||
|
AnimationCurve(const QEasingCurve &easing, const QPointF &start, const QPointF &end);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
|
bool isFromData() const;
|
||||||
|
|
||||||
double minimumTime() const;
|
double minimumTime() const;
|
||||||
|
|
||||||
double maximumTime() const;
|
double maximumTime() const;
|
||||||
@@ -48,6 +58,14 @@ public:
|
|||||||
|
|
||||||
double maximumValue() const;
|
double maximumValue() const;
|
||||||
|
|
||||||
|
CurveSegment segment(double time) const;
|
||||||
|
|
||||||
|
std::vector<CurveSegment> segments() const;
|
||||||
|
|
||||||
|
QPainterPath simplePath() const;
|
||||||
|
|
||||||
|
QPainterPath intersectionPath() const;
|
||||||
|
|
||||||
std::vector<Keyframe> keyframes() const;
|
std::vector<Keyframe> keyframes() const;
|
||||||
|
|
||||||
std::vector<QPointF> extrema() const;
|
std::vector<QPointF> extrema() const;
|
||||||
@@ -56,14 +74,22 @@ public:
|
|||||||
|
|
||||||
std::vector<double> xForY(double y, uint segment) const;
|
std::vector<double> xForY(double y, uint segment) const;
|
||||||
|
|
||||||
bool intersects(const QPointF &coord, double radius);
|
bool intersects(const QPointF &coord, double radiusX, double radiusY) const;
|
||||||
|
|
||||||
|
void append(const AnimationCurve &other);
|
||||||
|
|
||||||
|
void insert(double time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Keyframe> m_frames;
|
void analyze();
|
||||||
|
|
||||||
|
bool m_fromData;
|
||||||
|
|
||||||
double m_minY;
|
double m_minY;
|
||||||
|
|
||||||
double m_maxY;
|
double m_maxY;
|
||||||
|
|
||||||
|
std::vector<Keyframe> m_frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -29,8 +29,11 @@
|
|||||||
#include "detail/graphicsview.h"
|
#include "detail/graphicsview.h"
|
||||||
#include "detail/treeview.h"
|
#include "detail/treeview.h"
|
||||||
|
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
@@ -44,7 +47,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
|||||||
splitter->addWidget(m_view);
|
splitter->addWidget(m_view);
|
||||||
splitter->setStretchFactor(1, 2);
|
splitter->setStretchFactor(1, 2);
|
||||||
|
|
||||||
QHBoxLayout *box = new QHBoxLayout;
|
QVBoxLayout *box = new QVBoxLayout;
|
||||||
|
box->addWidget(createToolBar());
|
||||||
box->addWidget(splitter);
|
box->addWidget(splitter);
|
||||||
setLayout(box);
|
setLayout(box);
|
||||||
|
|
||||||
@@ -61,4 +65,62 @@ void CurveEditor::zoomY(double zoom)
|
|||||||
m_view->setZoomY(zoom);
|
m_view->setZoomY(zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CurveEditor::clearCanvas()
|
||||||
|
{
|
||||||
|
m_view->reset(m_tree->selection());
|
||||||
|
}
|
||||||
|
|
||||||
|
QToolBar *CurveEditor::createToolBar()
|
||||||
|
{
|
||||||
|
QToolBar *bar = new QToolBar;
|
||||||
|
bar->setFloatable(false);
|
||||||
|
|
||||||
|
QAction *tangentLinearAction = bar->addAction("Linear");
|
||||||
|
QAction *tangentStepAction = bar->addAction("Step");
|
||||||
|
QAction *tangentSplineAction = bar->addAction("Spline");
|
||||||
|
QAction *tangentDefaultAction = bar->addAction("Set Default");
|
||||||
|
|
||||||
|
auto setLinearInterpolation = [this]() {
|
||||||
|
m_view->setInterpolation(Keyframe::Interpolation::Linear);
|
||||||
|
};
|
||||||
|
auto setStepInterpolation = [this]() {
|
||||||
|
m_view->setInterpolation(Keyframe::Interpolation::Step);
|
||||||
|
};
|
||||||
|
auto setSplineInterpolation = [this]() {
|
||||||
|
m_view->setInterpolation(Keyframe::Interpolation::Bezier);
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(tangentLinearAction, &QAction::triggered, setLinearInterpolation);
|
||||||
|
connect(tangentStepAction, &QAction::triggered, setStepInterpolation);
|
||||||
|
connect(tangentSplineAction, &QAction::triggered, setSplineInterpolation);
|
||||||
|
|
||||||
|
Q_UNUSED(tangentLinearAction);
|
||||||
|
Q_UNUSED(tangentSplineAction);
|
||||||
|
Q_UNUSED(tangentStepAction);
|
||||||
|
Q_UNUSED(tangentDefaultAction);
|
||||||
|
|
||||||
|
auto *valueBox = new QHBoxLayout;
|
||||||
|
valueBox->addWidget(new QLabel(tr("Value")));
|
||||||
|
valueBox->addWidget(new QDoubleSpinBox);
|
||||||
|
auto *valueWidget = new QWidget;
|
||||||
|
valueWidget->setLayout(valueBox);
|
||||||
|
bar->addWidget(valueWidget);
|
||||||
|
|
||||||
|
auto *durationBox = new QHBoxLayout;
|
||||||
|
durationBox->addWidget(new QLabel(tr("Duration")));
|
||||||
|
durationBox->addWidget(new QSpinBox);
|
||||||
|
auto *durationWidget = new QWidget;
|
||||||
|
durationWidget->setLayout(durationBox);
|
||||||
|
bar->addWidget(durationWidget);
|
||||||
|
|
||||||
|
auto *positionBox = new QHBoxLayout;
|
||||||
|
positionBox->addWidget(new QLabel(tr("Current Frame")));
|
||||||
|
positionBox->addWidget(new QSpinBox);
|
||||||
|
auto *positionWidget = new QWidget;
|
||||||
|
positionWidget->setLayout(positionBox);
|
||||||
|
bar->addWidget(positionWidget);
|
||||||
|
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QToolBar>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
@@ -44,7 +45,11 @@ public:
|
|||||||
|
|
||||||
void zoomY(double zoom);
|
void zoomY(double zoom);
|
||||||
|
|
||||||
|
void clearCanvas();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QToolBar *createToolBar();
|
||||||
|
|
||||||
TreeView *m_tree;
|
TreeView *m_tree;
|
||||||
|
|
||||||
GraphicsView *m_view;
|
GraphicsView *m_view;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ HEADERS += \
|
|||||||
$$PWD/animationcurve.h \
|
$$PWD/animationcurve.h \
|
||||||
$$PWD/curveeditor.h \
|
$$PWD/curveeditor.h \
|
||||||
$$PWD/curveeditormodel.h \
|
$$PWD/curveeditormodel.h \
|
||||||
|
$$PWD/curvesegment.h \
|
||||||
$$PWD/detail/colorcontrol.h \
|
$$PWD/detail/colorcontrol.h \
|
||||||
$$PWD/detail/curveeditorstyledialog.h \
|
$$PWD/detail/curveeditorstyledialog.h \
|
||||||
$$PWD/detail/curveitem.h \
|
$$PWD/detail/curveitem.h \
|
||||||
$$PWD/detail/curvesegment.h \
|
|
||||||
$$PWD/detail/graphicsscene.h \
|
$$PWD/detail/graphicsscene.h \
|
||||||
$$PWD/detail/graphicsview.h \
|
$$PWD/detail/graphicsview.h \
|
||||||
$$PWD/detail/handleitem.h \
|
$$PWD/detail/handleitem.h \
|
||||||
@@ -24,12 +24,12 @@ HEADERS += \
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/animationcurve.cpp \
|
$$PWD/animationcurve.cpp \
|
||||||
|
$$PWD/curvesegment.cpp \
|
||||||
$$PWD/curveeditor.cpp \
|
$$PWD/curveeditor.cpp \
|
||||||
$$PWD/curveeditormodel.cpp \
|
$$PWD/curveeditormodel.cpp \
|
||||||
$$PWD/detail/colorcontrol.cpp \
|
$$PWD/detail/colorcontrol.cpp \
|
||||||
$$PWD/detail/curveeditorstyledialog.cpp \
|
$$PWD/detail/curveeditorstyledialog.cpp \
|
||||||
$$PWD/detail/curveitem.cpp \
|
$$PWD/detail/curveitem.cpp \
|
||||||
$$PWD/detail/curvesegment.cpp \
|
|
||||||
$$PWD/detail/graphicsscene.cpp \
|
$$PWD/detail/graphicsscene.cpp \
|
||||||
$$PWD/detail/graphicsview.cpp \
|
$$PWD/detail/graphicsview.cpp \
|
||||||
$$PWD/detail/handleitem.cpp \
|
$$PWD/detail/handleitem.cpp \
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ struct CurveItemStyleOption
|
|||||||
double width = 1.0;
|
double width = 1.0;
|
||||||
QColor color = QColor(0, 200, 0);
|
QColor color = QColor(0, 200, 0);
|
||||||
QColor selectionColor = QColor(200, 200, 200);
|
QColor selectionColor = QColor(200, 200, 200);
|
||||||
|
QColor easingCurveColor = QColor(200, 0, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PlayheadStyleOption
|
struct PlayheadStyleOption
|
||||||
@@ -84,6 +85,9 @@ struct Shortcuts
|
|||||||
Shortcut zoom = Shortcut(Qt::RightButton, Qt::AltModifier);
|
Shortcut zoom = Shortcut(Qt::RightButton, Qt::AltModifier);
|
||||||
Shortcut pan = Shortcut(Qt::MiddleButton, Qt::AltModifier);
|
Shortcut pan = Shortcut(Qt::MiddleButton, Qt::AltModifier);
|
||||||
Shortcut frameAll = Shortcut(Qt::NoModifier, Qt::Key_A);
|
Shortcut frameAll = Shortcut(Qt::NoModifier, Qt::Key_A);
|
||||||
|
|
||||||
|
Shortcut insertKeyframe = Shortcut(Qt::MiddleButton, Qt::NoModifier);
|
||||||
|
Shortcut deleteKeyframe = Shortcut(Qt::NoModifier, Qt::Key_Delete);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CurveEditorStyle
|
struct CurveEditorStyle
|
||||||
|
|||||||
500
src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp
Normal file
500
src/plugins/qmldesigner/components/curveeditor/curvesegment.cpp
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Design Tooling
|
||||||
|
**
|
||||||
|
** 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 "curvesegment.h"
|
||||||
|
#include "detail/utils.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <QEasingCurve>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <qmath.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace DesignTools {
|
||||||
|
|
||||||
|
class CubicPolynomial
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CubicPolynomial(double p0, double p1, double p2, double p3);
|
||||||
|
|
||||||
|
std::vector<double> extrema() const;
|
||||||
|
|
||||||
|
std::vector<double> roots() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_a;
|
||||||
|
double m_b;
|
||||||
|
double m_c;
|
||||||
|
double m_d;
|
||||||
|
};
|
||||||
|
|
||||||
|
CubicPolynomial::CubicPolynomial(double p0, double p1, double p2, double p3)
|
||||||
|
: m_a(p3 - 3.0 * p2 + 3.0 * p1 - p0)
|
||||||
|
, m_b(3.0 * p2 - 6.0 * p1 + 3.0 * p0)
|
||||||
|
, m_c(3.0 * p1 - 3.0 * p0)
|
||||||
|
, m_d(p0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<double> CubicPolynomial::extrema() const
|
||||||
|
{
|
||||||
|
std::vector<double> out;
|
||||||
|
|
||||||
|
auto addValidValue = [&out](double value) {
|
||||||
|
if (!std::isnan(value) && !std::isinf(value))
|
||||||
|
out.push_back(clamp(value, 0.0, 1.0));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find the roots of the first derivative of y.
|
||||||
|
auto pd2 = (2.0 * m_b) / (3.0 * m_a) / 2.0;
|
||||||
|
auto q = m_c / (3.0 * m_a);
|
||||||
|
|
||||||
|
auto radi = std::pow(pd2, 2.0) - q;
|
||||||
|
|
||||||
|
auto x1 = -pd2 + std::sqrt(radi);
|
||||||
|
auto x2 = -pd2 - std::sqrt(radi);
|
||||||
|
|
||||||
|
addValidValue(x1);
|
||||||
|
addValidValue(x2);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> CubicPolynomial::roots() const
|
||||||
|
{
|
||||||
|
std::vector<double> out;
|
||||||
|
|
||||||
|
auto addValidValue = [&out](double value) {
|
||||||
|
if (!(std::isnan(value) || std::isinf(value)))
|
||||||
|
out.push_back(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_a == 0.0) {
|
||||||
|
// Linear
|
||||||
|
if (m_b == 0.0) {
|
||||||
|
if (m_c != 0.0)
|
||||||
|
out.push_back(-m_d / m_c);
|
||||||
|
// Quadratic
|
||||||
|
} else {
|
||||||
|
const double p = m_c / m_b / 2.0;
|
||||||
|
const double q = m_d / m_b;
|
||||||
|
addValidValue(-p + std::sqrt(std::pow(p, 2.0) - q));
|
||||||
|
addValidValue(-p - std::sqrt(std::pow(p, 2.0) - q));
|
||||||
|
}
|
||||||
|
// Cubic
|
||||||
|
} else {
|
||||||
|
const double p = 3.0 * m_a * m_c - std::pow(m_b, 2.0);
|
||||||
|
const double q = 2.0 * std::pow(m_b, 3.0) - 9.0 * m_a * m_b * m_c
|
||||||
|
+ 27.0 * std::pow(m_a, 2.0) * m_d;
|
||||||
|
|
||||||
|
auto disc = std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0);
|
||||||
|
|
||||||
|
auto toX = [&](double y) { return (y - m_b) / (3.0 * m_a); };
|
||||||
|
|
||||||
|
// One real solution.
|
||||||
|
if (disc >= 0) {
|
||||||
|
auto u = (1.0 / 2.0)
|
||||||
|
* std::cbrt(-4.0 * q
|
||||||
|
+ 4.0 * std::sqrt(std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0)));
|
||||||
|
auto v = (1.0 / 2.0)
|
||||||
|
* std::cbrt(-4.0 * q
|
||||||
|
- 4.0 * std::sqrt(std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0)));
|
||||||
|
|
||||||
|
addValidValue(toX(u + v));
|
||||||
|
// Three real solutions.
|
||||||
|
} else {
|
||||||
|
auto phi = acos(-q / (2 * std::sqrt(-std::pow(p, 3.0))));
|
||||||
|
auto y1 = std::sqrt(-p) * 2.0 * cos(phi / 3.0);
|
||||||
|
auto y2 = std::sqrt(-p) * 2.0 * cos((phi / 3.0) + (2.0 * M_PI / 3.0));
|
||||||
|
auto y3 = std::sqrt(-p) * 2.0 * cos((phi / 3.0) + (4.0 * M_PI / 3.0));
|
||||||
|
|
||||||
|
addValidValue(toX(y1));
|
||||||
|
addValidValue(toX(y2));
|
||||||
|
addValidValue(toX(y3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurveSegment::CurveSegment()
|
||||||
|
: m_left()
|
||||||
|
, m_right()
|
||||||
|
{}
|
||||||
|
|
||||||
|
CurveSegment::CurveSegment(const Keyframe &left, const Keyframe &right)
|
||||||
|
: m_left(left)
|
||||||
|
, m_right(right)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool CurveSegment::isValid() const
|
||||||
|
{
|
||||||
|
return m_left.position() != m_right.position();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CurveSegment::containsX(double x) const
|
||||||
|
{
|
||||||
|
return m_left.position().x() <= x && m_right.position().x() >= x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyframe CurveSegment::left() const
|
||||||
|
{
|
||||||
|
return m_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyframe CurveSegment::right() const
|
||||||
|
{
|
||||||
|
return m_right;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyframe::Interpolation CurveSegment::interpolation() const
|
||||||
|
{
|
||||||
|
bool invalidBezier = m_right.interpolation() == Keyframe::Interpolation::Bezier
|
||||||
|
&& (!m_left.hasRightHandle() || !m_right.hasLeftHandle());
|
||||||
|
|
||||||
|
if (m_right.interpolation() == Keyframe::Interpolation::Undefined || invalidBezier)
|
||||||
|
return Keyframe::Interpolation::Linear;
|
||||||
|
|
||||||
|
return m_right.interpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
double evaluateForT(double t, double p0, double p1, double p2, double p3)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(t >= 0. && t <= 1., return 0.0);
|
||||||
|
|
||||||
|
const double it = 1.0 - t;
|
||||||
|
|
||||||
|
return p0 * std::pow(it, 3.0) + p1 * 3.0 * std::pow(it, 2.0) * t
|
||||||
|
+ p2 * 3.0 * it * std::pow(t, 2.0) + p3 * std::pow(t, 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF CurveSegment::evaluate(double t) const
|
||||||
|
{
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear) {
|
||||||
|
return lerp(t, m_left.position(), m_right.position());
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
if (t == 1.0)
|
||||||
|
return m_right.position();
|
||||||
|
|
||||||
|
QPointF br(m_right.position().x(), m_left.position().y());
|
||||||
|
return lerp(t, m_left.position(), br);
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
const double x = evaluateForT(t,
|
||||||
|
m_left.position().x(),
|
||||||
|
m_left.rightHandle().x(),
|
||||||
|
m_right.leftHandle().x(),
|
||||||
|
m_right.position().x());
|
||||||
|
|
||||||
|
const double y = evaluateForT(t,
|
||||||
|
m_left.position().y(),
|
||||||
|
m_left.rightHandle().y(),
|
||||||
|
m_right.leftHandle().y(),
|
||||||
|
m_right.position().y());
|
||||||
|
|
||||||
|
return QPointF(x, y);
|
||||||
|
}
|
||||||
|
return QPointF();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath CurveSegment::path() const
|
||||||
|
{
|
||||||
|
QPainterPath path(m_left.position());
|
||||||
|
extend(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveSegment::extend(QPainterPath &path) const
|
||||||
|
{
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear) {
|
||||||
|
path.lineTo(m_right.position());
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
path.lineTo(QPointF(m_right.position().x(), m_left.position().y()));
|
||||||
|
path.lineTo(m_right.position());
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
path.cubicTo(m_left.rightHandle(), m_right.leftHandle(), m_right.position());
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Easing) {
|
||||||
|
auto mapEasing = [](const QPointF &start, const QPointF &end, const QPointF &pos) {
|
||||||
|
QPointF slope(end.x() - start.x(), end.y() - start.y());
|
||||||
|
return QPointF(start.x() + slope.x() * pos.x(), start.y() + slope.y() * pos.y());
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariant data = m_right.data();
|
||||||
|
if (data.isValid() && data.type() == static_cast<int>(QMetaType::QEasingCurve)) {
|
||||||
|
QVector<QPointF> points = data.value<QEasingCurve>().toCubicSpline();
|
||||||
|
int numSegments = points.count() / 3;
|
||||||
|
for (int i = 0; i < numSegments; i++) {
|
||||||
|
QPointF p1 = mapEasing(m_left.position(), m_right.position(), points.at(i * 3));
|
||||||
|
QPointF p2 = mapEasing(m_left.position(), m_right.position(), points.at(i * 3 + 1));
|
||||||
|
QPointF p3 = mapEasing(m_left.position(), m_right.position(), points.at(i * 3 + 2));
|
||||||
|
path.cubicTo(p1, p2, p3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QEasingCurve CurveSegment::easingCurve() const
|
||||||
|
{
|
||||||
|
auto mapPosition = [this](const QPointF &position) {
|
||||||
|
QPointF min = m_left.position();
|
||||||
|
QPointF max = m_right.position();
|
||||||
|
return QPointF((position.x() - min.x()) / (max.x() - min.x()),
|
||||||
|
(position.y() - min.y()) / (max.y() - min.y()));
|
||||||
|
};
|
||||||
|
|
||||||
|
QEasingCurve curve;
|
||||||
|
curve.addCubicBezierSegment(mapPosition(m_left.rightHandle()),
|
||||||
|
mapPosition(m_right.leftHandle()),
|
||||||
|
mapPosition(m_right.position()));
|
||||||
|
|
||||||
|
return curve;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QPointF> CurveSegment::extrema() const
|
||||||
|
{
|
||||||
|
std::vector<QPointF> out;
|
||||||
|
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear
|
||||||
|
|| interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
out.push_back(left().position());
|
||||||
|
out.push_back(right().position());
|
||||||
|
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
auto polynomial = CubicPolynomial(m_left.position().y(),
|
||||||
|
m_left.rightHandle().y(),
|
||||||
|
m_right.leftHandle().y(),
|
||||||
|
m_right.position().y());
|
||||||
|
|
||||||
|
for (double t : polynomial.extrema()) {
|
||||||
|
const double x = evaluateForT(t,
|
||||||
|
m_left.position().x(),
|
||||||
|
m_left.rightHandle().x(),
|
||||||
|
m_right.leftHandle().x(),
|
||||||
|
m_right.position().x());
|
||||||
|
|
||||||
|
const double y = evaluateForT(t,
|
||||||
|
m_left.position().y(),
|
||||||
|
m_left.rightHandle().y(),
|
||||||
|
m_right.leftHandle().y(),
|
||||||
|
m_right.position().y());
|
||||||
|
|
||||||
|
out.push_back(QPointF(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> CurveSegment::tForX(double x) const
|
||||||
|
{
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear) {
|
||||||
|
return {reverseLerp(x, m_right.position().x(), m_left.position().x())};
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
return {reverseLerp(x, m_left.position().x(), m_right.position().x())};
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
auto polynomial = CubicPolynomial(m_left.position().x() - x,
|
||||||
|
m_left.rightHandle().x() - x,
|
||||||
|
m_right.leftHandle().x() - x,
|
||||||
|
m_right.position().x() - x);
|
||||||
|
|
||||||
|
std::vector<double> out;
|
||||||
|
for (double t : polynomial.roots()) {
|
||||||
|
if (t >= 0.0 && t <= 1.0)
|
||||||
|
out.push_back(t);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> CurveSegment::tForY(double y) const
|
||||||
|
{
|
||||||
|
auto polynomial = CubicPolynomial(m_left.position().y() - y,
|
||||||
|
m_left.rightHandle().y() - y,
|
||||||
|
m_right.leftHandle().y() - y,
|
||||||
|
m_right.position().y() - y);
|
||||||
|
|
||||||
|
std::vector<double> out;
|
||||||
|
for (double t : polynomial.roots()) {
|
||||||
|
if (t >= 0.0 && t <= 1.0)
|
||||||
|
out.push_back(t);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> CurveSegment::yForX(double x) const
|
||||||
|
{
|
||||||
|
std::vector<double> out;
|
||||||
|
|
||||||
|
auto polynomial = CubicPolynomial(m_left.position().x() - x,
|
||||||
|
m_left.rightHandle().x() - x,
|
||||||
|
m_right.leftHandle().x() - x,
|
||||||
|
m_right.position().x() - x);
|
||||||
|
|
||||||
|
for (double t : polynomial.roots()) {
|
||||||
|
if (t < 0.0 || t > 1.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const double y = evaluateForT(t,
|
||||||
|
m_left.position().y(),
|
||||||
|
m_left.rightHandle().y(),
|
||||||
|
m_right.leftHandle().y(),
|
||||||
|
m_right.position().y());
|
||||||
|
|
||||||
|
out.push_back(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> CurveSegment::xForY(double y) const
|
||||||
|
{
|
||||||
|
std::vector<double> out;
|
||||||
|
|
||||||
|
auto polynomial = CubicPolynomial(m_left.position().y() - y,
|
||||||
|
m_left.rightHandle().y() - y,
|
||||||
|
m_right.leftHandle().y() - y,
|
||||||
|
m_right.position().y() - y);
|
||||||
|
|
||||||
|
for (double t : polynomial.roots()) {
|
||||||
|
if (t < 0.0 || t > 1.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const double x = evaluateForT(t,
|
||||||
|
m_left.position().x(),
|
||||||
|
m_left.rightHandle().x(),
|
||||||
|
m_right.leftHandle().x(),
|
||||||
|
m_right.position().x());
|
||||||
|
|
||||||
|
out.push_back(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Keyframe, 3> CurveSegment::splitAt(double time)
|
||||||
|
{
|
||||||
|
std::array<Keyframe, 3> out;
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear) {
|
||||||
|
for (double t : tForX(time)) {
|
||||||
|
out[0] = left();
|
||||||
|
out[1] = Keyframe(lerp(t, left().position(), right().position()));
|
||||||
|
out[2] = right();
|
||||||
|
|
||||||
|
out[1].setInterpolation(Keyframe::Interpolation::Linear);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
out[0] = left();
|
||||||
|
out[1] = Keyframe(QPointF(left().position() + QPointF(time, 0.0)));
|
||||||
|
out[2] = right();
|
||||||
|
|
||||||
|
out[1].setInterpolation(Keyframe::Interpolation::Step);
|
||||||
|
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
for (double t : tForX(time)) {
|
||||||
|
auto p0 = lerp(t, left().position(), left().rightHandle());
|
||||||
|
auto p1 = lerp(t, left().rightHandle(), right().leftHandle());
|
||||||
|
auto p2 = lerp(t, right().leftHandle(), right().position());
|
||||||
|
|
||||||
|
auto p01 = lerp(t, p0, p1);
|
||||||
|
auto p12 = lerp(t, p1, p2);
|
||||||
|
auto p01p12 = lerp(t, p01, p12);
|
||||||
|
|
||||||
|
out[0] = Keyframe(left().position(), left().leftHandle(), p0);
|
||||||
|
out[1] = Keyframe(p01p12, p01, p12);
|
||||||
|
out[2] = Keyframe(right().position(), p2, right().rightHandle());
|
||||||
|
|
||||||
|
out[0].setInterpolation(left().interpolation());
|
||||||
|
out[0].setData(left().data());
|
||||||
|
|
||||||
|
out[2].setInterpolation(right().interpolation());
|
||||||
|
out[2].setData(right().data());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CurveSegment::intersects(const QPointF &coord, double radiusX, double radiusY) const
|
||||||
|
{
|
||||||
|
if (interpolation() == Keyframe::Interpolation::Linear) {
|
||||||
|
for (auto &t : tForX(coord.x())) {
|
||||||
|
QLineF line(evaluate(t), coord);
|
||||||
|
if (std::abs(line.dy()) < radiusY)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Step) {
|
||||||
|
if (coord.x() > (right().position().x() - radiusX))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (coord.y() > (left().position().y() - radiusY)
|
||||||
|
&& coord.y() < (left().position().y() + radiusY))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (interpolation() == Keyframe::Interpolation::Bezier) {
|
||||||
|
for (auto &y : yForX(coord.x())) {
|
||||||
|
QLineF line(coord.x(), y, coord.x(), coord.y());
|
||||||
|
if (line.length() < radiusY)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &x : xForY(coord.y())) {
|
||||||
|
QLineF line(x, coord.y(), coord.x(), coord.y());
|
||||||
|
if (line.length() < radiusX)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveSegment::setLeft(const Keyframe &frame)
|
||||||
|
{
|
||||||
|
m_left = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveSegment::setRight(const Keyframe &frame)
|
||||||
|
{
|
||||||
|
m_right = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveSegment::setInterpolation(const Keyframe::Interpolation &interpol)
|
||||||
|
{
|
||||||
|
m_right.setInterpolation(interpol);
|
||||||
|
|
||||||
|
if (interpol == Keyframe::Interpolation::Bezier) {
|
||||||
|
double distance = QLineF(m_left.position(), m_right.position()).length() / 3.0;
|
||||||
|
if (!m_left.hasRightHandle())
|
||||||
|
m_left.setRightHandle(m_left.position() + QPointF(distance, 0.0));
|
||||||
|
|
||||||
|
if (!m_right.hasLeftHandle())
|
||||||
|
m_right.setLeftHandle(m_right.position() - QPointF(distance, 0.0));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
m_left.setRightHandle(QPointF());
|
||||||
|
m_right.setLeftHandle(QPointF());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace DesignTools.
|
||||||
@@ -27,10 +27,13 @@
|
|||||||
|
|
||||||
#include "keyframe.h"
|
#include "keyframe.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QPointF;
|
class QPointF;
|
||||||
|
class QEasingCurve;
|
||||||
|
class QPainterPath;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
@@ -42,20 +45,44 @@ public:
|
|||||||
|
|
||||||
CurveSegment(const Keyframe &first, const Keyframe &last);
|
CurveSegment(const Keyframe &first, const Keyframe &last);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
bool containsX(double x) const;
|
bool containsX(double x) const;
|
||||||
|
|
||||||
|
Keyframe left() const;
|
||||||
|
|
||||||
|
Keyframe right() const;
|
||||||
|
|
||||||
|
Keyframe::Interpolation interpolation() const;
|
||||||
|
|
||||||
QPointF evaluate(double t) const;
|
QPointF evaluate(double t) const;
|
||||||
|
|
||||||
|
QPainterPath path() const;
|
||||||
|
|
||||||
|
void extend(QPainterPath &path) const;
|
||||||
|
|
||||||
|
QEasingCurve easingCurve() const;
|
||||||
|
|
||||||
std::vector<QPointF> extrema() const;
|
std::vector<QPointF> extrema() const;
|
||||||
|
|
||||||
|
std::vector<double> tForX(double x) const;
|
||||||
|
|
||||||
|
std::vector<double> tForY(double y) const;
|
||||||
|
|
||||||
std::vector<double> yForX(double x) const;
|
std::vector<double> yForX(double x) const;
|
||||||
|
|
||||||
std::vector<double> xForY(double y) const;
|
std::vector<double> xForY(double y) const;
|
||||||
|
|
||||||
|
std::array<Keyframe, 3> splitAt(double time);
|
||||||
|
|
||||||
|
bool intersects(const QPointF &coord, double radiusX, double radiusY) const;
|
||||||
|
|
||||||
void setLeft(const Keyframe &frame);
|
void setLeft(const Keyframe &frame);
|
||||||
|
|
||||||
void setRight(const Keyframe &frame);
|
void setRight(const Keyframe &frame);
|
||||||
|
|
||||||
|
void setInterpolation(const Keyframe::Interpolation &interpol);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Keyframe m_left;
|
Keyframe m_left;
|
||||||
|
|
||||||
@@ -28,9 +28,11 @@
|
|||||||
#include "keyframeitem.h"
|
#include "keyframeitem.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <QEasingCurve>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
@@ -39,42 +41,43 @@ CurveItem::CurveItem(QGraphicsItem *parent)
|
|||||||
: QGraphicsObject(parent)
|
: QGraphicsObject(parent)
|
||||||
, m_id(0)
|
, m_id(0)
|
||||||
, m_style()
|
, m_style()
|
||||||
|
, m_type(ValueType::Undefined)
|
||||||
|
, m_component(PropertyTreeItem::Component::Generic)
|
||||||
, m_transform()
|
, m_transform()
|
||||||
, m_keyframes()
|
, m_keyframes()
|
||||||
, m_underMouse(false)
|
, m_underMouse(false)
|
||||||
, m_itemDirty(false)
|
, m_itemDirty(false)
|
||||||
, m_pathDirty(true)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent)
|
CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent)
|
||||||
: QGraphicsObject(parent)
|
: QGraphicsObject(parent)
|
||||||
, m_id(id)
|
, m_id(id)
|
||||||
, m_style()
|
, m_style()
|
||||||
|
, m_type(ValueType::Undefined)
|
||||||
|
, m_component(PropertyTreeItem::Component::Generic)
|
||||||
, m_transform()
|
, m_transform()
|
||||||
, m_keyframes()
|
, m_keyframes()
|
||||||
, m_underMouse(false)
|
, m_underMouse(false)
|
||||||
, m_itemDirty(false)
|
, m_itemDirty(false)
|
||||||
, m_pathDirty(true)
|
|
||||||
{
|
{
|
||||||
setAcceptHoverEvents(true);
|
setAcceptHoverEvents(true);
|
||||||
|
|
||||||
setFlag(QGraphicsItem::ItemIsMovable, false);
|
setFlag(QGraphicsItem::ItemIsMovable, false);
|
||||||
|
|
||||||
auto emitCurveChanged = [this]() {
|
|
||||||
m_itemDirty = true;
|
|
||||||
m_pathDirty = true;
|
|
||||||
update();
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto frame : curve.keyframes()) {
|
for (auto frame : curve.keyframes()) {
|
||||||
auto *item = new KeyframeItem(frame, this);
|
auto *item = new KeyframeItem(frame, this);
|
||||||
QObject::connect(item, &KeyframeItem::redrawCurve, emitCurveChanged);
|
QObject::connect(item, &KeyframeItem::redrawCurve, this, &CurveItem::emitCurveChanged);
|
||||||
m_keyframes.push_back(item);
|
m_keyframes.push_back(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CurveItem::~CurveItem() {}
|
CurveItem::~CurveItem() {}
|
||||||
|
|
||||||
|
bool CurveItem::isUnderMouse() const
|
||||||
|
{
|
||||||
|
return m_underMouse;
|
||||||
|
}
|
||||||
|
|
||||||
int CurveItem::type() const
|
int CurveItem::type() const
|
||||||
{
|
{
|
||||||
return Type;
|
return Type;
|
||||||
@@ -100,10 +103,11 @@ bool CurveItem::contains(const QPointF &point) const
|
|||||||
bool valid = false;
|
bool valid = false;
|
||||||
QPointF transformed(m_transform.inverted(&valid).map(point));
|
QPointF transformed(m_transform.inverted(&valid).map(point));
|
||||||
|
|
||||||
double width = std::abs(20.0 / scaleY(m_transform));
|
double widthX = std::abs(10.0 / scaleX(m_transform));
|
||||||
|
double widthY = std::abs(10.0 / scaleY(m_transform));
|
||||||
|
|
||||||
if (valid)
|
if (valid)
|
||||||
return curve().intersects(transformed, width);
|
return curve().intersects(transformed, widthX, widthY);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -112,15 +116,25 @@ void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidg
|
|||||||
{
|
{
|
||||||
if (m_keyframes.size() > 1) {
|
if (m_keyframes.size() > 1) {
|
||||||
QPen pen = painter->pen();
|
QPen pen = painter->pen();
|
||||||
QColor col = m_underMouse ? Qt::red : m_style.color;
|
|
||||||
|
|
||||||
pen.setWidthF(m_style.width);
|
pen.setWidthF(m_style.width);
|
||||||
pen.setColor(hasSelection() ? m_style.selectionColor : col);
|
|
||||||
|
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->setPen(pen);
|
painter->setPen(pen);
|
||||||
painter->drawPath(path());
|
|
||||||
|
|
||||||
|
for (auto &&segment : curve().segments()) {
|
||||||
|
if (segment.interpolation() == Keyframe::Interpolation::Easing) {
|
||||||
|
pen.setColor(m_style.easingCurveColor);
|
||||||
|
} else {
|
||||||
|
if (m_underMouse)
|
||||||
|
pen.setColor(Qt::red);
|
||||||
|
else if (hasSelection())
|
||||||
|
pen.setColor(m_style.selectionColor);
|
||||||
|
else
|
||||||
|
pen.setColor(m_style.color);
|
||||||
|
}
|
||||||
|
painter->setPen(pen);
|
||||||
|
painter->drawPath(m_transform.map(segment.path()));
|
||||||
|
}
|
||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +150,6 @@ bool CurveItem::hasSelection() const
|
|||||||
if (frame->selected())
|
if (frame->selected())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,52 +158,145 @@ unsigned int CurveItem::id() const
|
|||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPainterPath CurveItem::path() const
|
ValueType CurveItem::valueType() const
|
||||||
{
|
{
|
||||||
if (m_pathDirty) {
|
return m_type;
|
||||||
Keyframe previous = m_keyframes.front()->keyframe();
|
}
|
||||||
Keyframe current;
|
|
||||||
|
|
||||||
m_path = QPainterPath(m_transform.map(previous.position()));
|
PropertyTreeItem::Component CurveItem::component() const
|
||||||
for (size_t i = 1; i < m_keyframes.size(); ++i) {
|
{
|
||||||
current = m_keyframes[i]->keyframe();
|
return m_component;
|
||||||
|
|
||||||
if (previous.rightHandle().isNull() || current.leftHandle().isNull()) {
|
|
||||||
m_path.lineTo(m_transform.map(current.position()));
|
|
||||||
} else {
|
|
||||||
m_path.cubicTo(
|
|
||||||
m_transform.map(previous.rightHandle()),
|
|
||||||
m_transform.map(current.leftHandle()),
|
|
||||||
m_transform.map(current.position()));
|
|
||||||
}
|
|
||||||
|
|
||||||
previous = current;
|
|
||||||
}
|
|
||||||
m_pathDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationCurve CurveItem::curve() const
|
AnimationCurve CurveItem::curve() const
|
||||||
{
|
{
|
||||||
std::vector<Keyframe> out;
|
std::vector<Keyframe> frames;
|
||||||
out.reserve(m_keyframes.size());
|
frames.reserve(m_keyframes.size());
|
||||||
for (auto item : m_keyframes)
|
for (auto *frameItem : m_keyframes)
|
||||||
out.push_back(item->keyframe());
|
frames.push_back(frameItem->keyframe());
|
||||||
|
|
||||||
|
return AnimationCurve(frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationCurve CurveItem::resolvedCurve() const
|
||||||
|
{
|
||||||
|
std::vector<AnimationCurve> tmp = curves();
|
||||||
|
|
||||||
|
if (tmp.size() == 0)
|
||||||
|
return AnimationCurve();
|
||||||
|
|
||||||
|
if (tmp.size() == 1)
|
||||||
|
return tmp[0];
|
||||||
|
|
||||||
|
AnimationCurve out = tmp[0];
|
||||||
|
for (size_t i = 1; i < tmp.size(); ++i)
|
||||||
|
out.append(tmp[i]);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<AnimationCurve> CurveItem::curves() const
|
||||||
|
{
|
||||||
|
std::vector<AnimationCurve> out;
|
||||||
|
|
||||||
|
std::vector<Keyframe> tmp;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_keyframes.size(); ++i) {
|
||||||
|
KeyframeItem *item = m_keyframes[i];
|
||||||
|
|
||||||
|
Keyframe current = item->keyframe();
|
||||||
|
|
||||||
|
if (current.interpolation() == Keyframe::Interpolation::Easing && i > 0) {
|
||||||
|
if (!tmp.empty()) {
|
||||||
|
Keyframe previous = tmp.back();
|
||||||
|
|
||||||
|
if (tmp.size() >= 2)
|
||||||
|
out.push_back(AnimationCurve(tmp));
|
||||||
|
|
||||||
|
out.push_back(
|
||||||
|
AnimationCurve(
|
||||||
|
current.data().value<QEasingCurve>(),
|
||||||
|
previous.position(),
|
||||||
|
current.position()));
|
||||||
|
|
||||||
|
tmp.clear();
|
||||||
|
tmp.push_back(current);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tmp.push_back(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp.size() >= 2)
|
||||||
|
out.push_back(AnimationCurve(tmp));
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::restore()
|
||||||
|
{
|
||||||
|
if (m_keyframes.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto byTime = [](auto a, auto b) {
|
||||||
|
return a->keyframe().position().x() < b->keyframe().position().x();
|
||||||
|
};
|
||||||
|
std::sort(m_keyframes.begin(), m_keyframes.end(), byTime);
|
||||||
|
|
||||||
|
KeyframeItem *prevItem = m_keyframes[0];
|
||||||
|
for (size_t i = 1; i < m_keyframes.size(); ++i) {
|
||||||
|
KeyframeItem *currItem = m_keyframes[i];
|
||||||
|
|
||||||
|
Keyframe prev = prevItem->keyframe();
|
||||||
|
Keyframe curr = currItem->keyframe();
|
||||||
|
CurveSegment segment(prev, curr);
|
||||||
|
|
||||||
|
segment.setInterpolation(segment.interpolation());
|
||||||
|
|
||||||
|
prevItem->setRightHandle(segment.left().rightHandle());
|
||||||
|
currItem->setLeftHandle(segment.right().leftHandle());
|
||||||
|
|
||||||
|
prevItem = currItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CurveItem::setDirty(bool dirty)
|
void CurveItem::setDirty(bool dirty)
|
||||||
{
|
{
|
||||||
m_itemDirty = dirty;
|
m_itemDirty = dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CurveItem::setHandleVisibility(bool visible)
|
||||||
|
{
|
||||||
|
for (auto frame : m_keyframes)
|
||||||
|
frame->setHandleVisibility(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::setValueType(ValueType type)
|
||||||
|
{
|
||||||
|
m_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::setComponent(PropertyTreeItem::Component comp)
|
||||||
|
{
|
||||||
|
m_component = comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::setCurve(const AnimationCurve &curve)
|
||||||
|
{
|
||||||
|
freeClear(m_keyframes);
|
||||||
|
|
||||||
|
for (auto frame : curve.keyframes()) {
|
||||||
|
auto *item = new KeyframeItem(frame, this);
|
||||||
|
item->setComponentTransform(m_transform);
|
||||||
|
m_keyframes.push_back(item);
|
||||||
|
QObject::connect(item, &KeyframeItem::redrawCurve, this, &CurveItem::emitCurveChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
emitCurveChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QRectF CurveItem::setComponentTransform(const QTransform &transform)
|
QRectF CurveItem::setComponentTransform(const QTransform &transform)
|
||||||
{
|
{
|
||||||
m_pathDirty = true;
|
|
||||||
|
|
||||||
prepareGeometryChange();
|
prepareGeometryChange();
|
||||||
m_transform = transform;
|
m_transform = transform;
|
||||||
for (auto frame : m_keyframes)
|
for (auto frame : m_keyframes)
|
||||||
@@ -207,6 +313,30 @@ void CurveItem::setStyle(const CurveEditorStyle &style)
|
|||||||
frame->setStyle(style);
|
frame->setStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
|
||||||
|
{
|
||||||
|
if (m_keyframes.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
KeyframeItem *prevItem = m_keyframes[0];
|
||||||
|
for (size_t i = 1; i < m_keyframes.size(); ++i) {
|
||||||
|
KeyframeItem *currItem = m_keyframes[i];
|
||||||
|
if (currItem->selected()) {
|
||||||
|
Keyframe prev = prevItem->keyframe();
|
||||||
|
Keyframe curr = currItem->keyframe();
|
||||||
|
CurveSegment segment(prev, curr);
|
||||||
|
|
||||||
|
segment.setInterpolation(interpolation);
|
||||||
|
prevItem->setKeyframe(segment.left());
|
||||||
|
currItem->setKeyframe(segment.right());
|
||||||
|
|
||||||
|
m_itemDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevItem = currItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CurveItem::connect(GraphicsScene *scene)
|
void CurveItem::connect(GraphicsScene *scene)
|
||||||
{
|
{
|
||||||
for (auto *frame : m_keyframes) {
|
for (auto *frame : m_keyframes) {
|
||||||
@@ -223,4 +353,31 @@ void CurveItem::setIsUnderMouse(bool under)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CurveItem::insertKeyframeByTime(double time)
|
||||||
|
{
|
||||||
|
AnimationCurve acurve = curve();
|
||||||
|
acurve.insert(time);
|
||||||
|
setCurve(acurve);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::deleteSelectedKeyframes()
|
||||||
|
{
|
||||||
|
for (auto *&item : m_keyframes) {
|
||||||
|
if (item->selected()) {
|
||||||
|
delete item;
|
||||||
|
item = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto isNullptr = [](KeyframeItem *frame) { return frame == nullptr; };
|
||||||
|
auto iter = std::remove_if(m_keyframes.begin(), m_keyframes.end(), isNullptr);
|
||||||
|
m_keyframes.erase(iter, m_keyframes.end());
|
||||||
|
emitCurveChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::emitCurveChanged()
|
||||||
|
{
|
||||||
|
m_itemDirty = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -26,7 +26,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "curveeditorstyle.h"
|
#include "curveeditorstyle.h"
|
||||||
|
#include "curvesegment.h"
|
||||||
|
#include "keyframe.h"
|
||||||
#include "selectableitem.h"
|
#include "selectableitem.h"
|
||||||
|
#include "treeitem.h"
|
||||||
|
|
||||||
#include <QGraphicsObject>
|
#include <QGraphicsObject>
|
||||||
|
|
||||||
@@ -59,29 +62,59 @@ public:
|
|||||||
|
|
||||||
bool isDirty() const;
|
bool isDirty() const;
|
||||||
|
|
||||||
|
bool isUnderMouse() const;
|
||||||
|
|
||||||
bool hasSelection() const;
|
bool hasSelection() const;
|
||||||
|
|
||||||
unsigned int id() const;
|
unsigned int id() const;
|
||||||
|
|
||||||
|
ValueType valueType() const;
|
||||||
|
|
||||||
|
PropertyTreeItem::Component component() const;
|
||||||
|
|
||||||
AnimationCurve curve() const;
|
AnimationCurve curve() const;
|
||||||
|
|
||||||
|
AnimationCurve resolvedCurve() const;
|
||||||
|
|
||||||
|
std::vector<AnimationCurve> curves() const;
|
||||||
|
|
||||||
|
void restore();
|
||||||
|
|
||||||
void setDirty(bool dirty);
|
void setDirty(bool dirty);
|
||||||
|
|
||||||
|
void setHandleVisibility(bool visible);
|
||||||
|
|
||||||
|
void setValueType(ValueType type);
|
||||||
|
|
||||||
|
void setComponent(PropertyTreeItem::Component comp);
|
||||||
|
|
||||||
|
void setCurve(const AnimationCurve &curve);
|
||||||
|
|
||||||
QRectF setComponentTransform(const QTransform &transform);
|
QRectF setComponentTransform(const QTransform &transform);
|
||||||
|
|
||||||
void setStyle(const CurveEditorStyle &style);
|
void setStyle(const CurveEditorStyle &style);
|
||||||
|
|
||||||
|
void setInterpolation(Keyframe::Interpolation interpolation);
|
||||||
|
|
||||||
void connect(GraphicsScene *scene);
|
void connect(GraphicsScene *scene);
|
||||||
|
|
||||||
void setIsUnderMouse(bool under);
|
void setIsUnderMouse(bool under);
|
||||||
|
|
||||||
|
void insertKeyframeByTime(double time);
|
||||||
|
|
||||||
|
void deleteSelectedKeyframes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPainterPath path() const;
|
void emitCurveChanged();
|
||||||
|
|
||||||
unsigned int m_id;
|
unsigned int m_id;
|
||||||
|
|
||||||
CurveItemStyleOption m_style;
|
CurveItemStyleOption m_style;
|
||||||
|
|
||||||
|
ValueType m_type;
|
||||||
|
|
||||||
|
PropertyTreeItem::Component m_component;
|
||||||
|
|
||||||
QTransform m_transform;
|
QTransform m_transform;
|
||||||
|
|
||||||
std::vector<KeyframeItem *> m_keyframes;
|
std::vector<KeyframeItem *> m_keyframes;
|
||||||
@@ -89,10 +122,6 @@ private:
|
|||||||
bool m_underMouse;
|
bool m_underMouse;
|
||||||
|
|
||||||
bool m_itemDirty;
|
bool m_itemDirty;
|
||||||
|
|
||||||
mutable bool m_pathDirty;
|
|
||||||
|
|
||||||
mutable QPainterPath m_path;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -1,277 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the Qt Design Tooling
|
|
||||||
**
|
|
||||||
** 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 "curvesegment.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include <qmath.h>
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
namespace DesignTools {
|
|
||||||
|
|
||||||
class CubicPolynomial
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CubicPolynomial(double p0, double p1, double p2, double p3);
|
|
||||||
|
|
||||||
std::vector<double> extrema() const;
|
|
||||||
|
|
||||||
std::vector<double> roots() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
double m_a;
|
|
||||||
double m_b;
|
|
||||||
double m_c;
|
|
||||||
double m_d;
|
|
||||||
};
|
|
||||||
|
|
||||||
CubicPolynomial::CubicPolynomial(double p0, double p1, double p2, double p3)
|
|
||||||
: m_a(p3 - 3.0 * p2 + 3.0 * p1 - p0)
|
|
||||||
, m_b(3.0 * p2 - 6.0 * p1 + 3.0 * p0)
|
|
||||||
, m_c(3.0 * p1 - 3.0 * p0)
|
|
||||||
, m_d(p0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::vector<double> CubicPolynomial::extrema() const
|
|
||||||
{
|
|
||||||
std::vector<double> out;
|
|
||||||
|
|
||||||
auto addValidValue = [&out](double value) {
|
|
||||||
if (!std::isnan(value) && !std::isinf(value))
|
|
||||||
out.push_back(clamp(value, 0.0, 1.0));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find the roots of the first derivative of y.
|
|
||||||
auto pd2 = (2.0 * m_b) / (3.0 * m_a) / 2.0;
|
|
||||||
auto q = m_c / (3.0 * m_a);
|
|
||||||
|
|
||||||
auto radi = std::pow(pd2, 2.0) - q;
|
|
||||||
|
|
||||||
auto x1 = -pd2 + std::sqrt(radi);
|
|
||||||
auto x2 = -pd2 - std::sqrt(radi);
|
|
||||||
|
|
||||||
addValidValue(x1);
|
|
||||||
addValidValue(x2);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> CubicPolynomial::roots() const
|
|
||||||
{
|
|
||||||
std::vector<double> out;
|
|
||||||
|
|
||||||
auto addValidValue = [&out](double value) {
|
|
||||||
if (!(std::isnan(value) || std::isinf(value)))
|
|
||||||
out.push_back(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (m_a == 0.0) {
|
|
||||||
// Linear
|
|
||||||
if (m_b == 0.0) {
|
|
||||||
if (m_c != 0.0)
|
|
||||||
out.push_back(-m_d / m_c);
|
|
||||||
// Quadratic
|
|
||||||
} else {
|
|
||||||
const double p = m_c / m_b / 2.0;
|
|
||||||
const double q = m_d / m_b;
|
|
||||||
addValidValue(-p + std::sqrt(std::pow(p, 2.0) - q));
|
|
||||||
addValidValue(-p - std::sqrt(std::pow(p, 2.0) - q));
|
|
||||||
}
|
|
||||||
// Cubic
|
|
||||||
} else {
|
|
||||||
const double p = 3.0 * m_a * m_c - std::pow(m_b, 2.0);
|
|
||||||
const double q = 2.0 * std::pow(m_b, 3.0) - 9.0 * m_a * m_b * m_c
|
|
||||||
+ 27.0 * std::pow(m_a, 2.0) * m_d;
|
|
||||||
|
|
||||||
auto disc = std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0);
|
|
||||||
|
|
||||||
auto toX = [&](double y) { return (y - m_b) / (3.0 * m_a); };
|
|
||||||
|
|
||||||
// One real solution.
|
|
||||||
if (disc >= 0) {
|
|
||||||
auto u = (1.0 / 2.0)
|
|
||||||
* std::cbrt(-4.0 * q
|
|
||||||
+ 4.0 * std::sqrt(std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0)));
|
|
||||||
auto v = (1.0 / 2.0)
|
|
||||||
* std::cbrt(-4.0 * q
|
|
||||||
- 4.0 * std::sqrt(std::pow(q, 2.0) + 4.0 * std::pow(p, 3.0)));
|
|
||||||
|
|
||||||
addValidValue(toX(u + v));
|
|
||||||
// Three real solutions.
|
|
||||||
} else {
|
|
||||||
auto phi = acos(-q / (2 * std::sqrt(-std::pow(p, 3.0))));
|
|
||||||
auto y1 = std::sqrt(-p) * 2.0 * cos(phi / 3.0);
|
|
||||||
auto y2 = std::sqrt(-p) * 2.0 * cos((phi / 3.0) + (2.0 * M_PI / 3.0));
|
|
||||||
auto y3 = std::sqrt(-p) * 2.0 * cos((phi / 3.0) + (4.0 * M_PI / 3.0));
|
|
||||||
|
|
||||||
addValidValue(toX(y1));
|
|
||||||
addValidValue(toX(y2));
|
|
||||||
addValidValue(toX(y3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurveSegment::CurveSegment()
|
|
||||||
: m_left()
|
|
||||||
, m_right()
|
|
||||||
{}
|
|
||||||
|
|
||||||
CurveSegment::CurveSegment(const Keyframe &left, const Keyframe &right)
|
|
||||||
: m_left(left)
|
|
||||||
, m_right(right)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool CurveSegment::containsX(double x) const
|
|
||||||
{
|
|
||||||
return m_left.position().x() <= x && m_right.position().x() >= x;
|
|
||||||
}
|
|
||||||
|
|
||||||
double evaluateForT(double t, double p0, double p1, double p2, double p3)
|
|
||||||
{
|
|
||||||
assert(t >= 0. && t <= 1.);
|
|
||||||
|
|
||||||
const double it = 1.0 - t;
|
|
||||||
|
|
||||||
return p0 * std::pow(it, 3.0) + p1 * 3.0 * std::pow(it, 2.0) * t
|
|
||||||
+ p2 * 3.0 * it * std::pow(t, 2.0) + p3 * std::pow(t, 3.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF CurveSegment::evaluate(double t) const
|
|
||||||
{
|
|
||||||
const double x = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().x(),
|
|
||||||
m_left.rightHandle().x(),
|
|
||||||
m_right.leftHandle().x(),
|
|
||||||
m_right.position().x());
|
|
||||||
|
|
||||||
const double y = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().y(),
|
|
||||||
m_left.rightHandle().y(),
|
|
||||||
m_right.leftHandle().y(),
|
|
||||||
m_right.position().y());
|
|
||||||
|
|
||||||
return QPointF(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QPointF> CurveSegment::extrema() const
|
|
||||||
{
|
|
||||||
std::vector<QPointF> out;
|
|
||||||
|
|
||||||
auto polynomial = CubicPolynomial(
|
|
||||||
m_left.position().y(),
|
|
||||||
m_left.rightHandle().y(),
|
|
||||||
m_right.leftHandle().y(),
|
|
||||||
m_right.position().y());
|
|
||||||
|
|
||||||
for (double t : polynomial.extrema()) {
|
|
||||||
|
|
||||||
const double x = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().x(),
|
|
||||||
m_left.rightHandle().x(),
|
|
||||||
m_right.leftHandle().x(),
|
|
||||||
m_right.position().x());
|
|
||||||
|
|
||||||
const double y = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().y(),
|
|
||||||
m_left.rightHandle().y(),
|
|
||||||
m_right.leftHandle().y(),
|
|
||||||
m_right.position().y());
|
|
||||||
|
|
||||||
out.push_back(QPointF(x, y));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> CurveSegment::yForX(double x) const
|
|
||||||
{
|
|
||||||
std::vector<double> out;
|
|
||||||
|
|
||||||
auto polynomial = CubicPolynomial(
|
|
||||||
m_left.position().x() - x,
|
|
||||||
m_left.rightHandle().x() - x,
|
|
||||||
m_right.leftHandle().x() - x,
|
|
||||||
m_right.position().x() - x);
|
|
||||||
|
|
||||||
for (double t : polynomial.roots()) {
|
|
||||||
if (t < 0.0 || t > 1.0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const double y = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().y(),
|
|
||||||
m_left.rightHandle().y(),
|
|
||||||
m_right.leftHandle().y(),
|
|
||||||
m_right.position().y());
|
|
||||||
|
|
||||||
out.push_back(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> CurveSegment::xForY(double y) const
|
|
||||||
{
|
|
||||||
std::vector<double> out;
|
|
||||||
|
|
||||||
auto polynomial = CubicPolynomial(
|
|
||||||
m_left.position().y() - y,
|
|
||||||
m_left.rightHandle().y() - y,
|
|
||||||
m_right.leftHandle().y() - y,
|
|
||||||
m_right.position().y() - y);
|
|
||||||
|
|
||||||
for (double t : polynomial.roots()) {
|
|
||||||
if (t < 0.0 || t > 1.0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const double x = evaluateForT(
|
|
||||||
t,
|
|
||||||
m_left.position().x(),
|
|
||||||
m_left.rightHandle().x(),
|
|
||||||
m_right.leftHandle().x(),
|
|
||||||
m_right.position().x());
|
|
||||||
|
|
||||||
out.push_back(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveSegment::setLeft(const Keyframe &frame)
|
|
||||||
{
|
|
||||||
m_left = frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveSegment::setRight(const Keyframe &frame)
|
|
||||||
{
|
|
||||||
m_right = frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
|
||||||
@@ -141,6 +141,10 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
|
|||||||
const auto itemList = items();
|
const auto itemList = items();
|
||||||
for (auto *item : itemList) {
|
for (auto *item : itemList) {
|
||||||
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
|
||||||
|
|
||||||
|
// CurveItems might becom invalid after a keyframe-drag operation.
|
||||||
|
curveItem->restore();
|
||||||
|
|
||||||
if (curveItem->contains(mouseEvent->scenePos()))
|
if (curveItem->contains(mouseEvent->scenePos()))
|
||||||
curveItem->setSelected(true);
|
curveItem->setSelected(true);
|
||||||
|
|
||||||
@@ -201,7 +205,7 @@ QRectF GraphicsScene::limits() const
|
|||||||
const auto itemList = items();
|
const auto itemList = items();
|
||||||
for (auto *item : itemList) {
|
for (auto *item : itemList) {
|
||||||
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
|
||||||
auto curve = curveItem->curve();
|
auto curve = curveItem->resolvedCurve();
|
||||||
if (min.x() > curve.minimumTime())
|
if (min.x() > curve.minimumTime())
|
||||||
min.rx() = curve.minimumTime();
|
min.rx() = curve.minimumTime();
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,18 @@ void GraphicsView::reset(const std::vector<CurveItem *> &items)
|
|||||||
viewport()->update();
|
viewport()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphicsView::setInterpolation(Keyframe::Interpolation interpol)
|
||||||
|
{
|
||||||
|
const auto itemList = items();
|
||||||
|
for (auto *item : itemList) {
|
||||||
|
if (auto *citem = qgraphicsitem_cast<CurveItem *>(item))
|
||||||
|
if (citem->hasSelection())
|
||||||
|
citem->setInterpolation(interpol);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsView::resizeEvent(QResizeEvent *event)
|
void GraphicsView::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
QGraphicsView::resizeEvent(event);
|
QGraphicsView::resizeEvent(event);
|
||||||
@@ -220,6 +232,8 @@ void GraphicsView::keyPressEvent(QKeyEvent *event)
|
|||||||
Shortcut shortcut(event->modifiers(), static_cast<Qt::Key>(event->key()));
|
Shortcut shortcut(event->modifiers(), static_cast<Qt::Key>(event->key()));
|
||||||
if (shortcut == m_style.shortcuts.frameAll)
|
if (shortcut == m_style.shortcuts.frameAll)
|
||||||
applyZoom(0.0, 0.0);
|
applyZoom(0.0, 0.0);
|
||||||
|
else if (shortcut == m_style.shortcuts.deleteKeyframe)
|
||||||
|
deleteSelectedKeyframes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsView::mousePressEvent(QMouseEvent *event)
|
void GraphicsView::mousePressEvent(QMouseEvent *event)
|
||||||
@@ -228,6 +242,11 @@ void GraphicsView::mousePressEvent(QMouseEvent *event)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Shortcut shortcut(event);
|
Shortcut shortcut(event);
|
||||||
|
if (shortcut == m_style.shortcuts.insertKeyframe) {
|
||||||
|
insertKeyframe(globalToRaster(event->globalPos()).x());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (shortcut == Shortcut(Qt::LeftButton)) {
|
if (shortcut == Shortcut(Qt::LeftButton)) {
|
||||||
QPointF pos = mapToScene(event->pos());
|
QPointF pos = mapToScene(event->pos());
|
||||||
if (timeScaleRect().contains(pos)) {
|
if (timeScaleRect().contains(pos)) {
|
||||||
@@ -355,6 +374,7 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
|||||||
double minValue = minimumValue();
|
double minValue = minimumValue();
|
||||||
double maxValue = maximumValue();
|
double maxValue = maximumValue();
|
||||||
|
|
||||||
|
|
||||||
QRectF canvas = canvasRect();
|
QRectF canvas = canvasRect();
|
||||||
|
|
||||||
double xZoomedOut = canvas.width() / (maxTime - minTime);
|
double xZoomedOut = canvas.width() / (maxTime - minTime);
|
||||||
@@ -366,7 +386,6 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
|||||||
double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn);
|
double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn);
|
||||||
|
|
||||||
m_transform = QTransform::fromScale(scaleX, scaleY);
|
m_transform = QTransform::fromScale(scaleX, scaleY);
|
||||||
|
|
||||||
m_scene.setComponentTransform(m_transform);
|
m_scene.setComponentTransform(m_transform);
|
||||||
|
|
||||||
QRectF sr = m_scene.sceneRect().adjusted(
|
QRectF sr = m_scene.sceneRect().adjusted(
|
||||||
@@ -385,13 +404,32 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphicsView::insertKeyframe(double time)
|
||||||
|
{
|
||||||
|
const auto itemList = items();
|
||||||
|
for (auto *item : itemList) {
|
||||||
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
|
||||||
|
if (curveItem->isUnderMouse())
|
||||||
|
curveItem->insertKeyframeByTime(std::round(time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsView::deleteSelectedKeyframes()
|
||||||
|
{
|
||||||
|
const auto itemList = items();
|
||||||
|
for (auto *item : itemList) {
|
||||||
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
|
||||||
|
curveItem->deleteSelectedKeyframes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
|
void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
|
||||||
{
|
{
|
||||||
QRectF gridRect = rect.adjusted(
|
QRectF gridRect = rect.adjusted(m_style.valueAxisWidth + m_style.canvasMargin,
|
||||||
m_style.valueAxisWidth + m_style.canvasMargin,
|
m_style.timeAxisHeight + m_style.canvasMargin,
|
||||||
m_style.timeAxisHeight + m_style.canvasMargin,
|
-m_style.canvasMargin,
|
||||||
-m_style.canvasMargin,
|
-m_style.canvasMargin);
|
||||||
-m_style.canvasMargin);
|
|
||||||
|
|
||||||
if (!gridRect.isValid())
|
if (!gridRect.isValid())
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ public:
|
|||||||
|
|
||||||
void reset(const std::vector<CurveItem *> &items);
|
void reset(const std::vector<CurveItem *> &items);
|
||||||
|
|
||||||
|
void setInterpolation(Keyframe::Interpolation interpol);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
@@ -122,6 +124,10 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void applyZoom(double x, double y, const QPoint &pivot = QPoint());
|
void applyZoom(double x, double y, const QPoint &pivot = QPoint());
|
||||||
|
|
||||||
|
void insertKeyframe(double time);
|
||||||
|
|
||||||
|
void deleteSelectedKeyframes();
|
||||||
|
|
||||||
void drawGrid(QPainter *painter, const QRectF &rect);
|
void drawGrid(QPainter *painter, const QRectF &rect);
|
||||||
|
|
||||||
void drawExtremaX(QPainter *painter, const QRectF &rect);
|
void drawExtremaX(QPainter *painter, const QRectF &rect);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "handleitem.h"
|
#include "handleitem.h"
|
||||||
|
#include "keyframeitem.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -101,4 +102,24 @@ void HandleItem::setStyle(const CurveEditorStyle &style)
|
|||||||
m_style = style.handleStyle;
|
m_style = style.handleStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant HandleItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
|
||||||
|
{
|
||||||
|
if (change == ItemPositionChange) {
|
||||||
|
if (KeyframeItem *parent = qgraphicsitem_cast<KeyframeItem *>(parentItem())) {
|
||||||
|
HandleSlot slot = parent->handleSlot(this);
|
||||||
|
QPointF pos = value.toPointF();
|
||||||
|
if (slot == HandleSlot::Left) {
|
||||||
|
if (pos.x() > 0.0)
|
||||||
|
pos.rx() = 0.0;
|
||||||
|
|
||||||
|
} else if (slot == HandleSlot::Right) {
|
||||||
|
if (pos.x() < 0.0)
|
||||||
|
pos.rx() = 0.0;
|
||||||
|
}
|
||||||
|
return QVariant(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QGraphicsItem::itemChange(change, value);
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public:
|
|||||||
|
|
||||||
void setStyle(const CurveEditorStyle &style);
|
void setStyle(const CurveEditorStyle &style);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HandleItemStyleOption m_style;
|
HandleItemStyleOption m_style;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "keyframeitem.h"
|
#include "keyframeitem.h"
|
||||||
|
#include "curveitem.h"
|
||||||
#include "handleitem.h"
|
#include "handleitem.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -40,31 +41,11 @@ KeyframeItem::KeyframeItem(QGraphicsItem *parent)
|
|||||||
KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent)
|
KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent)
|
||||||
: SelectableItem(parent)
|
: SelectableItem(parent)
|
||||||
, m_transform()
|
, m_transform()
|
||||||
, m_frame(keyframe)
|
, m_frame()
|
||||||
, m_left(keyframe.hasLeftHandle() ? new HandleItem(this) : nullptr)
|
, m_left(nullptr)
|
||||||
, m_right(keyframe.hasRightHandle() ? new HandleItem(this) : nullptr)
|
, m_right(nullptr)
|
||||||
{
|
{
|
||||||
auto updatePosition = [this]() { this->updatePosition(true); };
|
setKeyframe(keyframe);
|
||||||
connect(this, &QGraphicsObject::xChanged, updatePosition);
|
|
||||||
connect(this, &QGraphicsObject::yChanged, updatePosition);
|
|
||||||
|
|
||||||
if (m_left) {
|
|
||||||
m_left->setPos(m_frame.leftHandle() - m_frame.position());
|
|
||||||
auto updateLeftHandle = [this]() { updateHandle(m_left); };
|
|
||||||
connect(m_left, &QGraphicsObject::xChanged, updateLeftHandle);
|
|
||||||
connect(m_left, &QGraphicsObject::yChanged, updateLeftHandle);
|
|
||||||
m_left->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_right) {
|
|
||||||
m_right->setPos(m_frame.rightHandle() - m_frame.position());
|
|
||||||
auto updateRightHandle = [this]() { updateHandle(m_right); };
|
|
||||||
connect(m_right, &QGraphicsObject::xChanged, updateRightHandle);
|
|
||||||
connect(m_right, &QGraphicsObject::yChanged, updateRightHandle);
|
|
||||||
m_right->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
setPos(m_frame.position());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyframeItem::type() const
|
int KeyframeItem::type() const
|
||||||
@@ -101,6 +82,33 @@ Keyframe KeyframeItem::keyframe() const
|
|||||||
return m_frame;
|
return m_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HandleSlot KeyframeItem::handleSlot(HandleItem *item) const
|
||||||
|
{
|
||||||
|
if (item == m_left)
|
||||||
|
return HandleSlot::Left;
|
||||||
|
else if (item == m_right)
|
||||||
|
return HandleSlot::Right;
|
||||||
|
else
|
||||||
|
return HandleSlot::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::setHandleVisibility(bool visible)
|
||||||
|
{
|
||||||
|
m_visibleOverride = visible;
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
if (m_left)
|
||||||
|
m_left->show();
|
||||||
|
if (m_right)
|
||||||
|
m_right->show();
|
||||||
|
} else {
|
||||||
|
if (m_left)
|
||||||
|
m_left->hide();
|
||||||
|
if (m_right)
|
||||||
|
m_right->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void KeyframeItem::setComponentTransform(const QTransform &transform)
|
void KeyframeItem::setComponentTransform(const QTransform &transform)
|
||||||
{
|
{
|
||||||
m_transform = transform;
|
m_transform = transform;
|
||||||
@@ -125,6 +133,59 @@ void KeyframeItem::setStyle(const CurveEditorStyle &style)
|
|||||||
m_right->setStyle(style);
|
m_right->setStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::setKeyframe(const Keyframe &keyframe)
|
||||||
|
{
|
||||||
|
bool needsConnection = m_frame.position().isNull();
|
||||||
|
|
||||||
|
m_frame = keyframe;
|
||||||
|
|
||||||
|
if (needsConnection) {
|
||||||
|
auto updatePosition = [this]() { this->updatePosition(true); };
|
||||||
|
connect(this, &QGraphicsObject::xChanged, updatePosition);
|
||||||
|
connect(this, &QGraphicsObject::yChanged, updatePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_frame.hasLeftHandle()) {
|
||||||
|
if (!m_left) {
|
||||||
|
m_left = new HandleItem(this);
|
||||||
|
auto updateLeftHandle = [this]() { updateHandle(m_left); };
|
||||||
|
connect(m_left, &QGraphicsObject::xChanged, updateLeftHandle);
|
||||||
|
connect(m_left, &QGraphicsObject::yChanged, updateLeftHandle);
|
||||||
|
}
|
||||||
|
m_left->setPos(m_transform.map(m_frame.leftHandle() - m_frame.position()));
|
||||||
|
} else if (m_left) {
|
||||||
|
delete m_left;
|
||||||
|
m_left = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_frame.hasRightHandle()) {
|
||||||
|
if (!m_right) {
|
||||||
|
m_right = new HandleItem(this);
|
||||||
|
auto updateRightHandle = [this]() { updateHandle(m_right); };
|
||||||
|
connect(m_right, &QGraphicsObject::xChanged, updateRightHandle);
|
||||||
|
connect(m_right, &QGraphicsObject::yChanged, updateRightHandle);
|
||||||
|
}
|
||||||
|
m_right->setPos(m_transform.map(m_frame.rightHandle() - m_frame.position()));
|
||||||
|
} else if (m_right) {
|
||||||
|
delete m_right;
|
||||||
|
m_right = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPos(m_transform.map(m_frame.position()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::setLeftHandle(const QPointF &pos)
|
||||||
|
{
|
||||||
|
m_frame.setLeftHandle(pos);
|
||||||
|
setKeyframe(m_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::setRightHandle(const QPointF &pos)
|
||||||
|
{
|
||||||
|
m_frame.setRightHandle(pos);
|
||||||
|
setKeyframe(m_frame);
|
||||||
|
}
|
||||||
|
|
||||||
void KeyframeItem::updatePosition(bool update)
|
void KeyframeItem::updatePosition(bool update)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -160,6 +221,9 @@ void KeyframeItem::moveKeyframe(const QPointF &direction)
|
|||||||
void KeyframeItem::moveHandle(HandleSlot handle, double deltaAngle, double deltaLength)
|
void KeyframeItem::moveHandle(HandleSlot handle, double deltaAngle, double deltaLength)
|
||||||
{
|
{
|
||||||
auto move = [this, deltaAngle, deltaLength](HandleItem *item) {
|
auto move = [this, deltaAngle, deltaLength](HandleItem *item) {
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
QLineF current(QPointF(0.0, 0.0), item->pos());
|
QLineF current(QPointF(0.0, 0.0), item->pos());
|
||||||
current.setAngle(current.angle() + deltaAngle);
|
current.setAngle(current.angle() + deltaAngle);
|
||||||
current.setLength(current.length() + deltaLength);
|
current.setLength(current.length() + deltaLength);
|
||||||
@@ -225,6 +289,14 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
|
|||||||
QPointF position = m_transform.inverted(&ok).map(value.toPointF());
|
QPointF position = m_transform.inverted(&ok).map(value.toPointF());
|
||||||
if (ok) {
|
if (ok) {
|
||||||
position.setX(std::round(position.x()));
|
position.setX(std::round(position.x()));
|
||||||
|
|
||||||
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(parentItem())) {
|
||||||
|
if (curveItem->valueType() == ValueType::Integer)
|
||||||
|
position.setY(std::round(position.y()));
|
||||||
|
else if (curveItem->valueType() == ValueType::Bool)
|
||||||
|
position.setY(position.y() > 0.5 ? 1.0 : 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
return QVariant(m_transform.map(position));
|
return QVariant(m_transform.map(position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,19 +304,33 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
|
|||||||
return QGraphicsItem::itemChange(change, value);
|
return QGraphicsItem::itemChange(change, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
SelectableItem::mousePressEvent(event);
|
||||||
|
|
||||||
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(parentItem()))
|
||||||
|
curveItem->setHandleVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
SelectableItem::mouseReleaseEvent(event);
|
||||||
|
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(parentItem()))
|
||||||
|
curveItem->setHandleVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
void KeyframeItem::selectionCallback()
|
void KeyframeItem::selectionCallback()
|
||||||
{
|
{
|
||||||
auto setHandleVisibility = [](HandleItem *handle, bool visible) {
|
|
||||||
if (handle)
|
|
||||||
handle->setVisible(visible);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (selected()) {
|
if (selected()) {
|
||||||
setHandleVisibility(m_left, true);
|
if (m_visibleOverride) {
|
||||||
setHandleVisibility(m_right, true);
|
setHandleVisibility(true);
|
||||||
|
setHandleVisibility(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setHandleVisibility(m_left, false);
|
if (!m_visibleOverride) {
|
||||||
setHandleVisibility(m_right, false);
|
setHandleVisibility(false);
|
||||||
|
setHandleVisibility(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,10 +65,20 @@ public:
|
|||||||
|
|
||||||
Keyframe keyframe() const;
|
Keyframe keyframe() const;
|
||||||
|
|
||||||
|
HandleSlot handleSlot(HandleItem *item) const;
|
||||||
|
|
||||||
|
void setHandleVisibility(bool visible);
|
||||||
|
|
||||||
void setComponentTransform(const QTransform &transform);
|
void setComponentTransform(const QTransform &transform);
|
||||||
|
|
||||||
void setStyle(const CurveEditorStyle &style);
|
void setStyle(const CurveEditorStyle &style);
|
||||||
|
|
||||||
|
void setKeyframe(const Keyframe &keyframe);
|
||||||
|
|
||||||
|
void setLeftHandle(const QPointF &pos);
|
||||||
|
|
||||||
|
void setRightHandle(const QPointF &pos);
|
||||||
|
|
||||||
void moveKeyframe(const QPointF &direction);
|
void moveKeyframe(const QPointF &direction);
|
||||||
|
|
||||||
void moveHandle(HandleSlot handle, double deltaAngle, double deltaLength);
|
void moveHandle(HandleSlot handle, double deltaAngle, double deltaLength);
|
||||||
@@ -76,6 +86,10 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||||
|
|
||||||
|
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
|
|
||||||
|
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
|
|
||||||
void selectionCallback() override;
|
void selectionCallback() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -93,6 +107,8 @@ private:
|
|||||||
HandleItem *m_left;
|
HandleItem *m_left;
|
||||||
|
|
||||||
HandleItem *m_right;
|
HandleItem *m_right;
|
||||||
|
|
||||||
|
bool m_visibleOverride = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -26,8 +26,6 @@
|
|||||||
#include "selectableitem.h"
|
#include "selectableitem.h"
|
||||||
#include "keyframeitem.h"
|
#include "keyframeitem.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
SelectableItem::SelectableItem(QGraphicsItem *parent)
|
SelectableItem::SelectableItem(QGraphicsItem *parent)
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playh
|
|||||||
if (m_mouseInit.isNull())
|
if (m_mouseInit.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QPointF delta = event->globalPos() - m_mouseInit;
|
if ((event->globalPos() - m_mouseInit).manhattanLength() < QApplication::startDragDistance())
|
||||||
if (delta.manhattanLength() < QApplication::startDragDistance())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
QPointF delta = event->globalPos() - m_mouseCurr;
|
||||||
if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
|
if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
|
||||||
|| m_shortcut == m_shortcuts.removeFromSelection
|
|| m_shortcut == m_shortcuts.removeFromSelection
|
||||||
|| m_shortcut == m_shortcuts.toggleSelection) {
|
|| m_shortcut == m_shortcuts.toggleSelection) {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "treemodel.h"
|
#include "treemodel.h"
|
||||||
#include "treeitem.h"
|
|
||||||
#include "detail/graphicsview.h"
|
#include "detail/graphicsview.h"
|
||||||
|
#include "treeitem.h"
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ TreeView::TreeView(CurveEditorModel *model, QWidget *parent)
|
|||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
setHeaderHidden(true);
|
setHeaderHidden(true);
|
||||||
|
|
||||||
model->setParent(this);
|
|
||||||
setModel(model);
|
setModel(model);
|
||||||
|
|
||||||
auto expandItems = [this]() { expandAll(); };
|
auto expandItems = [this]() { expandAll(); };
|
||||||
@@ -54,7 +53,10 @@ TreeView::TreeView(CurveEditorModel *model, QWidget *parent)
|
|||||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &TreeView::changeSelection);
|
connect(selectionModel(),
|
||||||
|
&QItemSelectionModel::selectionChanged,
|
||||||
|
this,
|
||||||
|
&TreeView::changeSelection);
|
||||||
setStyle(model->style());
|
setStyle(model->style());
|
||||||
|
|
||||||
header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
@@ -83,6 +85,19 @@ void TreeView::setStyle(const CurveEditorStyle &style)
|
|||||||
delegate->setStyle(style);
|
delegate->setStyle(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<CurveItem *> TreeView::selection()
|
||||||
|
{
|
||||||
|
std::vector<DesignTools::CurveItem *> items;
|
||||||
|
for (auto &&index : selectionModel()->selectedRows(0)) {
|
||||||
|
if (index.isValid()) {
|
||||||
|
auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
|
||||||
|
if (auto *propertyItem = treeItem->asPropertyItem())
|
||||||
|
items.push_back(new CurveItem(treeItem->id(), propertyItem->curve()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
void TreeView::changeCurve(unsigned int id, const AnimationCurve &curve)
|
void TreeView::changeCurve(unsigned int id, const AnimationCurve &curve)
|
||||||
{
|
{
|
||||||
if (auto *curveModel = qobject_cast<CurveEditorModel *>(model()))
|
if (auto *curveModel = qobject_cast<CurveEditorModel *>(model()))
|
||||||
@@ -98,8 +113,12 @@ void TreeView::changeSelection(const QItemSelection &selected, const QItemSelect
|
|||||||
for (auto index : selectedIndexes()) {
|
for (auto index : selectedIndexes()) {
|
||||||
if (index.isValid() && index.column() == 0) {
|
if (index.isValid() && index.column() == 0) {
|
||||||
auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
|
auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
|
||||||
if (auto *propertyItem = treeItem->asPropertyItem())
|
if (auto *propertyItem = treeItem->asPropertyItem()) {
|
||||||
curves.push_back(new CurveItem(treeItem->id(), propertyItem->curve()));
|
auto *citem = new CurveItem(treeItem->id(), propertyItem->curve());
|
||||||
|
citem->setValueType(propertyItem->valueType());
|
||||||
|
citem->setComponent(propertyItem->component());
|
||||||
|
curves.push_back(citem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ public:
|
|||||||
|
|
||||||
void setStyle(const CurveEditorStyle &style);
|
void setStyle(const CurveEditorStyle &style);
|
||||||
|
|
||||||
|
std::vector<CurveItem *> selection();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSize sizeHint() const override;
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
|||||||
@@ -31,16 +31,6 @@
|
|||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
double clamp(double val, double lo, double hi)
|
|
||||||
{
|
|
||||||
return val < lo ? lo : (val > hi ? hi : val);
|
|
||||||
}
|
|
||||||
|
|
||||||
double lerp(double blend, double a, double b)
|
|
||||||
{
|
|
||||||
return (1.0 - blend) * a + blend * b;
|
|
||||||
}
|
|
||||||
|
|
||||||
double scaleX(const QTransform &transform)
|
double scaleX(const QTransform &transform)
|
||||||
{
|
{
|
||||||
return transform.m11();
|
return transform.m11();
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QColor;
|
class QColor;
|
||||||
class QPalette;
|
class QPalette;
|
||||||
@@ -33,12 +35,10 @@ class QRectF;
|
|||||||
class QTransform;
|
class QTransform;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
double clamp(double val, double lo, double hi);
|
|
||||||
|
|
||||||
double lerp(double blend, double a, double b);
|
|
||||||
|
|
||||||
double scaleX(const QTransform &transform);
|
double scaleX(const QTransform &transform);
|
||||||
|
|
||||||
double scaleY(const QTransform &transform);
|
double scaleY(const QTransform &transform);
|
||||||
@@ -49,4 +49,30 @@ QRectF bbox(const QRectF &rect, const QTransform &transform);
|
|||||||
|
|
||||||
QPalette singleColorPalette(const QColor &color);
|
QPalette singleColorPalette(const QColor &color);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void freeClear(std::vector<T *> &vec)
|
||||||
|
{
|
||||||
|
for (auto *&el : vec)
|
||||||
|
delete el;
|
||||||
|
vec.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TV, typename TC>
|
||||||
|
inline double clamp(const TV &val, const TC &lo, const TC &hi)
|
||||||
|
{
|
||||||
|
return val < lo ? lo : (val > hi ? hi : val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T lerp(double blend, const T &a, const T &b)
|
||||||
|
{
|
||||||
|
return (1.0 - blend) * a + blend * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T reverseLerp(double blend, const T &a, const T &b)
|
||||||
|
{
|
||||||
|
return (blend - b) / (a - b);
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -28,23 +28,49 @@
|
|||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
Keyframe::Keyframe()
|
Keyframe::Keyframe()
|
||||||
: m_position()
|
: m_interpolation(Interpolation::Undefined)
|
||||||
|
, m_position()
|
||||||
, m_leftHandle()
|
, m_leftHandle()
|
||||||
, m_rightHandle()
|
, m_rightHandle()
|
||||||
|
, m_data()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Keyframe::Keyframe(const QPointF &position)
|
Keyframe::Keyframe(const QPointF &position)
|
||||||
: m_position(position)
|
: m_interpolation(Interpolation::Linear)
|
||||||
|
, m_position(position)
|
||||||
, m_leftHandle()
|
, m_leftHandle()
|
||||||
, m_rightHandle()
|
, m_rightHandle()
|
||||||
|
, m_data()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
Keyframe::Keyframe(const QPointF &position, const QVariant &data)
|
||||||
|
: m_interpolation(Interpolation::Undefined)
|
||||||
|
, m_position(position)
|
||||||
|
, m_leftHandle()
|
||||||
|
, m_rightHandle()
|
||||||
|
, m_data()
|
||||||
|
{
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
Keyframe::Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle)
|
Keyframe::Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle)
|
||||||
: m_position(position)
|
: m_interpolation(Interpolation::Bezier)
|
||||||
|
, m_position(position)
|
||||||
, m_leftHandle(leftHandle)
|
, m_leftHandle(leftHandle)
|
||||||
, m_rightHandle(rightHandle)
|
, m_rightHandle(rightHandle)
|
||||||
|
, m_data()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool Keyframe::isValid() const
|
||||||
|
{
|
||||||
|
return m_interpolation != Interpolation::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Keyframe::hasData() const
|
||||||
|
{
|
||||||
|
return m_data.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
bool Keyframe::hasLeftHandle() const
|
bool Keyframe::hasLeftHandle() const
|
||||||
{
|
{
|
||||||
return !m_leftHandle.isNull();
|
return !m_leftHandle.isNull();
|
||||||
@@ -70,6 +96,21 @@ QPointF Keyframe::rightHandle() const
|
|||||||
return m_rightHandle;
|
return m_rightHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant Keyframe::data() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyframe::Interpolation Keyframe::interpolation() const
|
||||||
|
{
|
||||||
|
return m_interpolation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keyframe::setInterpolation(Interpolation interpol)
|
||||||
|
{
|
||||||
|
m_interpolation = interpol;
|
||||||
|
}
|
||||||
|
|
||||||
void Keyframe::setPosition(const QPointF &pos)
|
void Keyframe::setPosition(const QPointF &pos)
|
||||||
{
|
{
|
||||||
m_position = pos;
|
m_position = pos;
|
||||||
@@ -85,4 +126,30 @@ void Keyframe::setRightHandle(const QPointF &pos)
|
|||||||
m_rightHandle = pos;
|
m_rightHandle = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Keyframe::setData(const QVariant &data)
|
||||||
|
{
|
||||||
|
if (data.type() == static_cast<int>(QMetaType::QEasingCurve))
|
||||||
|
m_interpolation = Interpolation::Easing;
|
||||||
|
|
||||||
|
m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(Keyframe::Interpolation interpol)
|
||||||
|
{
|
||||||
|
switch (interpol) {
|
||||||
|
case Keyframe::Interpolation::Undefined:
|
||||||
|
return "Interpolation::Undefined";
|
||||||
|
case Keyframe::Interpolation::Step:
|
||||||
|
return "Interpolation::Step";
|
||||||
|
case Keyframe::Interpolation::Linear:
|
||||||
|
return "Interpolation::Linear";
|
||||||
|
case Keyframe::Interpolation::Bezier:
|
||||||
|
return "Interpolation::Bezier";
|
||||||
|
case Keyframe::Interpolation::Easing:
|
||||||
|
return "Interpolation::Easing";
|
||||||
|
default:
|
||||||
|
return "Interpolation::Undefined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -26,18 +26,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
namespace DesignTools {
|
namespace DesignTools {
|
||||||
|
|
||||||
class Keyframe
|
class Keyframe
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class Interpolation
|
||||||
|
{
|
||||||
|
Undefined,
|
||||||
|
Step,
|
||||||
|
Linear,
|
||||||
|
Bezier,
|
||||||
|
Easing
|
||||||
|
};
|
||||||
|
|
||||||
Keyframe();
|
Keyframe();
|
||||||
|
|
||||||
Keyframe(const QPointF &position);
|
Keyframe(const QPointF &position);
|
||||||
|
|
||||||
|
Keyframe(const QPointF &position, const QVariant& data);
|
||||||
|
|
||||||
Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle);
|
Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
bool hasData() const;
|
||||||
|
|
||||||
bool hasLeftHandle() const;
|
bool hasLeftHandle() const;
|
||||||
|
|
||||||
bool hasRightHandle() const;
|
bool hasRightHandle() const;
|
||||||
@@ -48,18 +64,32 @@ public:
|
|||||||
|
|
||||||
QPointF rightHandle() const;
|
QPointF rightHandle() const;
|
||||||
|
|
||||||
|
QVariant data() const;
|
||||||
|
|
||||||
|
Interpolation interpolation() const;
|
||||||
|
|
||||||
void setPosition(const QPointF &pos);
|
void setPosition(const QPointF &pos);
|
||||||
|
|
||||||
void setLeftHandle(const QPointF &pos);
|
void setLeftHandle(const QPointF &pos);
|
||||||
|
|
||||||
void setRightHandle(const QPointF &pos);
|
void setRightHandle(const QPointF &pos);
|
||||||
|
|
||||||
|
void setData(const QVariant& data);
|
||||||
|
|
||||||
|
void setInterpolation(Interpolation interpol);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Interpolation m_interpolation = Interpolation::Undefined;
|
||||||
|
|
||||||
QPointF m_position;
|
QPointF m_position;
|
||||||
|
|
||||||
QPointF m_leftHandle;
|
QPointF m_leftHandle;
|
||||||
|
|
||||||
QPointF m_rightHandle;
|
QPointF m_rightHandle;
|
||||||
|
|
||||||
|
QVariant m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string toString(Keyframe::Interpolation interpol);
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ TreeItem::~TreeItem()
|
|||||||
m_parent = nullptr;
|
m_parent = nullptr;
|
||||||
|
|
||||||
qDeleteAll(m_children);
|
qDeleteAll(m_children);
|
||||||
|
m_children.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon TreeItem::icon() const
|
QIcon TreeItem::icon() const
|
||||||
@@ -66,6 +67,16 @@ unsigned int TreeItem::id() const
|
|||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString TreeItem::name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TreeItem::hasChildren() const
|
||||||
|
{
|
||||||
|
return !m_children.empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool TreeItem::locked() const
|
bool TreeItem::locked() const
|
||||||
{
|
{
|
||||||
return m_locked;
|
return m_locked;
|
||||||
@@ -185,7 +196,6 @@ void TreeItem::setPinned(bool pinned)
|
|||||||
m_pinned = pinned;
|
m_pinned = pinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NodeTreeItem::NodeTreeItem(const QString &name, const QIcon &icon)
|
NodeTreeItem::NodeTreeItem(const QString &name, const QIcon &icon)
|
||||||
: TreeItem(name)
|
: TreeItem(name)
|
||||||
, m_icon(icon)
|
, m_icon(icon)
|
||||||
@@ -203,9 +213,12 @@ QIcon NodeTreeItem::icon() const
|
|||||||
return m_icon;
|
return m_icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PropertyTreeItem::PropertyTreeItem(const QString &name,
|
||||||
PropertyTreeItem::PropertyTreeItem(const QString &name, const AnimationCurve &curve)
|
const AnimationCurve &curve,
|
||||||
|
const ValueType &type)
|
||||||
: TreeItem(name)
|
: TreeItem(name)
|
||||||
|
, m_type(type)
|
||||||
|
, m_component(Component::Generic)
|
||||||
, m_curve(curve)
|
, m_curve(curve)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -214,6 +227,27 @@ PropertyTreeItem *PropertyTreeItem::asPropertyItem()
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NodeTreeItem *PropertyTreeItem::parentNodeTreeItem() const
|
||||||
|
{
|
||||||
|
TreeItem *p = parent();
|
||||||
|
while (p) {
|
||||||
|
if (NodeTreeItem *ni = p->asNodeItem())
|
||||||
|
return ni;
|
||||||
|
p = p->parent();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueType PropertyTreeItem::valueType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyTreeItem::Component PropertyTreeItem::component() const
|
||||||
|
{
|
||||||
|
return m_component;
|
||||||
|
}
|
||||||
|
|
||||||
AnimationCurve PropertyTreeItem::curve() const
|
AnimationCurve PropertyTreeItem::curve() const
|
||||||
{
|
{
|
||||||
return m_curve;
|
return m_curve;
|
||||||
@@ -224,4 +258,9 @@ void PropertyTreeItem::setCurve(const AnimationCurve &curve)
|
|||||||
m_curve = curve;
|
m_curve = curve;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PropertyTreeItem::setComponent(const Component &comp)
|
||||||
|
{
|
||||||
|
m_component = comp;
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace DesignTools.
|
} // End namespace DesignTools.
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ public:
|
|||||||
|
|
||||||
unsigned int id() const;
|
unsigned int id() const;
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
|
||||||
|
bool hasChildren() const;
|
||||||
|
|
||||||
bool locked() const;
|
bool locked() const;
|
||||||
|
|
||||||
bool pinned() const;
|
bool pinned() const;
|
||||||
@@ -102,7 +106,6 @@ protected:
|
|||||||
std::vector<TreeItem *> m_children;
|
std::vector<TreeItem *> m_children;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class NodeTreeItem : public TreeItem
|
class NodeTreeItem : public TreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -116,21 +119,42 @@ private:
|
|||||||
QIcon m_icon;
|
QIcon m_icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ValueType {
|
||||||
|
Undefined,
|
||||||
|
Bool,
|
||||||
|
Integer,
|
||||||
|
Double,
|
||||||
|
};
|
||||||
|
|
||||||
class PropertyTreeItem : public TreeItem
|
class PropertyTreeItem : public TreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PropertyTreeItem(const QString &name, const AnimationCurve &curve);
|
enum class Component { Generic, R, G, B, A, X, Y, Z, W };
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertyTreeItem(const QString &name, const AnimationCurve &curve, const ValueType &type);
|
||||||
|
|
||||||
PropertyTreeItem *asPropertyItem() override;
|
PropertyTreeItem *asPropertyItem() override;
|
||||||
|
|
||||||
|
const NodeTreeItem *parentNodeTreeItem() const;
|
||||||
|
|
||||||
|
ValueType valueType() const;
|
||||||
|
|
||||||
|
Component component() const;
|
||||||
|
|
||||||
AnimationCurve curve() const;
|
AnimationCurve curve() const;
|
||||||
|
|
||||||
void setCurve(const AnimationCurve &curve);
|
void setCurve(const AnimationCurve &curve);
|
||||||
|
|
||||||
|
void setComponent(const Component &comp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using TreeItem::addChild;
|
using TreeItem::addChild;
|
||||||
|
|
||||||
|
ValueType m_type = ValueType::Undefined;
|
||||||
|
|
||||||
|
Component m_component = Component::Generic;
|
||||||
|
|
||||||
AnimationCurve m_curve;
|
AnimationCurve m_curve;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 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 "animationcurvedialog.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
AnimationCurveDialog::AnimationCurveDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_editor(nullptr)
|
||||||
|
{
|
||||||
|
setWindowFlag(Qt::WindowStaysOnTopHint, true);
|
||||||
|
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationCurveDialog::AnimationCurveDialog(DesignTools::CurveEditorModel *model, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_editor(nullptr)
|
||||||
|
{
|
||||||
|
setWindowFlag(Qt::WindowStaysOnTopHint, true);
|
||||||
|
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||||
|
setModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveDialog::setModel(DesignTools::CurveEditorModel *model)
|
||||||
|
{
|
||||||
|
if (m_editor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_editor = new DesignTools::CurveEditor(model);
|
||||||
|
|
||||||
|
auto *layout = new QVBoxLayout;
|
||||||
|
layout->addWidget(m_editor);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveDialog::showEvent(QShowEvent *)
|
||||||
|
{
|
||||||
|
m_editor->clearCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 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 "curveeditor/curveeditor.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class AnimationCurveDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimationCurveDialog(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
AnimationCurveDialog(DesignTools::CurveEditorModel *model, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setModel(DesignTools::CurveEditorModel *model);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DesignTools::CurveEditor *m_editor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 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 "animationcurveeditormodel.h"
|
||||||
|
#include "curveeditor/curveeditorstyle.h"
|
||||||
|
#include "curveeditor/treeitem.h"
|
||||||
|
#include "easingcurve.h"
|
||||||
|
#include "qmltimeline.h"
|
||||||
|
|
||||||
|
#include <bindingproperty.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
AnimationCurveEditorModel::AnimationCurveEditorModel(double minTime, double maxTime)
|
||||||
|
: CurveEditorModel()
|
||||||
|
, m_minTime(minTime)
|
||||||
|
, m_maxTime(maxTime)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AnimationCurveEditorModel::~AnimationCurveEditorModel() {}
|
||||||
|
|
||||||
|
double AnimationCurveEditorModel::minimumTime() const
|
||||||
|
{
|
||||||
|
return m_minTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
double AnimationCurveEditorModel::maximumTime() const
|
||||||
|
{
|
||||||
|
return m_maxTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignTools::CurveEditorStyle AnimationCurveEditorModel::style() const
|
||||||
|
{
|
||||||
|
// Pseudo auto generated. See: CurveEditorStyleDialog
|
||||||
|
DesignTools::CurveEditorStyle out;
|
||||||
|
out.backgroundBrush = QBrush(QColor(55, 55, 55));
|
||||||
|
out.backgroundAlternateBrush = QBrush(QColor(0, 0, 50));
|
||||||
|
out.fontColor = QColor(255, 255, 255);
|
||||||
|
out.gridColor = QColor(114, 116, 118);
|
||||||
|
out.canvasMargin = 15;
|
||||||
|
out.zoomInWidth = 99;
|
||||||
|
out.zoomInHeight = 99;
|
||||||
|
out.timeAxisHeight = 40;
|
||||||
|
out.timeOffsetLeft = 10;
|
||||||
|
out.timeOffsetRight = 10;
|
||||||
|
out.rangeBarColor = QColor(46, 47, 48);
|
||||||
|
out.rangeBarCapsColor = QColor(50, 50, 255);
|
||||||
|
out.valueAxisWidth = 60;
|
||||||
|
out.valueOffsetTop = 10;
|
||||||
|
out.valueOffsetBottom = 10;
|
||||||
|
out.handleStyle.size = 12;
|
||||||
|
out.handleStyle.lineWidth = 1;
|
||||||
|
out.handleStyle.color = QColor(255, 255, 255);
|
||||||
|
out.handleStyle.selectionColor = QColor(255, 255, 255);
|
||||||
|
out.keyframeStyle.size = 13;
|
||||||
|
out.keyframeStyle.color = QColor(172, 210, 255);
|
||||||
|
out.keyframeStyle.selectionColor = QColor(255, 255, 255);
|
||||||
|
out.curveStyle.width = 1;
|
||||||
|
out.curveStyle.color = QColor(0, 200, 0);
|
||||||
|
out.curveStyle.selectionColor = QColor(255, 255, 255);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveEditorModel::setTimeline(const QmlTimeline &timeline)
|
||||||
|
{
|
||||||
|
m_minTime = timeline.startKeyframe();
|
||||||
|
m_maxTime = timeline.endKeyframe();
|
||||||
|
|
||||||
|
std::vector<DesignTools::TreeItem *> items;
|
||||||
|
for (auto &&target : timeline.allTargets())
|
||||||
|
if (DesignTools::TreeItem *item = createTopLevelItem(timeline, target))
|
||||||
|
items.push_back(item);
|
||||||
|
|
||||||
|
reset(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveEditorModel::setMinimumTime(double time)
|
||||||
|
{
|
||||||
|
m_minTime = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveEditorModel::setMaximumTime(double time)
|
||||||
|
{
|
||||||
|
m_maxTime = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignTools::ValueType typeFrom(const QmlTimelineKeyframeGroup &group)
|
||||||
|
{
|
||||||
|
if (group.valueType() == TypeName("double") || group.valueType() == TypeName("real"))
|
||||||
|
return DesignTools::ValueType::Double;
|
||||||
|
|
||||||
|
if (group.valueType() == TypeName("bool"))
|
||||||
|
return DesignTools::ValueType::Bool;
|
||||||
|
|
||||||
|
if (group.valueType() == TypeName("integer"))
|
||||||
|
return DesignTools::ValueType::Integer;
|
||||||
|
|
||||||
|
return DesignTools::ValueType::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignTools::TreeItem *AnimationCurveEditorModel::createTopLevelItem(const QmlTimeline &timeline,
|
||||||
|
const ModelNode &node)
|
||||||
|
{
|
||||||
|
if (!node.isValid())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto *nodeItem = new DesignTools::NodeTreeItem(node.id(), QIcon(":/ICON_INSTANCE"));
|
||||||
|
for (auto &&grp : timeline.keyframeGroupsForTarget(node)) {
|
||||||
|
if (grp.isValid()) {
|
||||||
|
DesignTools::AnimationCurve curve = createAnimationCurve(grp);
|
||||||
|
if (curve.isValid()) {
|
||||||
|
QString name = QString::fromUtf8(grp.propertyName());
|
||||||
|
nodeItem->addChild(new DesignTools::PropertyTreeItem(name, curve, typeFrom(grp)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nodeItem->hasChildren()) {
|
||||||
|
delete nodeItem;
|
||||||
|
nodeItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignTools::AnimationCurve AnimationCurveEditorModel::createAnimationCurve(
|
||||||
|
const QmlTimelineKeyframeGroup &group)
|
||||||
|
{
|
||||||
|
switch (typeFrom(group)) {
|
||||||
|
case DesignTools::ValueType::Bool:
|
||||||
|
return createDoubleCurve(group);
|
||||||
|
|
||||||
|
case DesignTools::ValueType::Integer:
|
||||||
|
return createDoubleCurve(group);
|
||||||
|
|
||||||
|
case DesignTools::ValueType::Double:
|
||||||
|
return createDoubleCurve(group);
|
||||||
|
default:
|
||||||
|
return DesignTools::AnimationCurve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DesignTools::AnimationCurve AnimationCurveEditorModel::createDoubleCurve(
|
||||||
|
const QmlTimelineKeyframeGroup &group)
|
||||||
|
{
|
||||||
|
std::vector<DesignTools::Keyframe> keyframes;
|
||||||
|
for (auto &&frame : group.keyframePositions()) {
|
||||||
|
QVariant timeVariant = frame.variantProperty("frame").value();
|
||||||
|
QVariant valueVariant = frame.variantProperty("value").value();
|
||||||
|
|
||||||
|
if (timeVariant.isValid() && valueVariant.isValid()) {
|
||||||
|
QPointF position(timeVariant.toDouble(), valueFromVariant(valueVariant));
|
||||||
|
auto keyframe = DesignTools::Keyframe(position);
|
||||||
|
|
||||||
|
if (frame.hasBindingProperty("easing.bezierCurve")) {
|
||||||
|
EasingCurve ecurve;
|
||||||
|
ecurve.fromString(frame.bindingProperty("easing.bezierCurve").expression());
|
||||||
|
keyframe.setData(static_cast<QEasingCurve>(ecurve));
|
||||||
|
}
|
||||||
|
|
||||||
|
keyframes.push_back(keyframe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DesignTools::AnimationCurve(keyframes);
|
||||||
|
}
|
||||||
|
|
||||||
|
double AnimationCurveEditorModel::valueFromVariant(const QVariant &variant)
|
||||||
|
{
|
||||||
|
return variant.toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationCurveEditorModel::reset(const std::vector<DesignTools::TreeItem *> &items)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
unsigned int counter = 0;
|
||||||
|
for (auto *item : items) {
|
||||||
|
item->setId(++counter);
|
||||||
|
root()->addChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Design Tooling
|
||||||
|
**
|
||||||
|
** 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 "curveeditor/curveeditormodel.h"
|
||||||
|
#include "curveeditor/treeitem.h"
|
||||||
|
|
||||||
|
#include <qmltimelinekeyframegroup.h>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class AnimationCurveEditorModel : public DesignTools::CurveEditorModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimationCurveEditorModel(double minTime, double maxTime);
|
||||||
|
|
||||||
|
~AnimationCurveEditorModel() override;
|
||||||
|
|
||||||
|
double minimumTime() const override;
|
||||||
|
|
||||||
|
double maximumTime() const override;
|
||||||
|
|
||||||
|
DesignTools::CurveEditorStyle style() const override;
|
||||||
|
|
||||||
|
void setTimeline(const QmlTimeline &timeline);
|
||||||
|
|
||||||
|
void setMinimumTime(double time);
|
||||||
|
|
||||||
|
void setMaximumTime(double time);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DesignTools::TreeItem *createTopLevelItem(const QmlTimeline &timeline, const ModelNode &node);
|
||||||
|
|
||||||
|
DesignTools::AnimationCurve createAnimationCurve(const QmlTimelineKeyframeGroup &group);
|
||||||
|
|
||||||
|
DesignTools::AnimationCurve createDoubleCurve(const QmlTimelineKeyframeGroup &group);
|
||||||
|
|
||||||
|
double valueFromVariant(const QVariant &variant);
|
||||||
|
|
||||||
|
void reset(const std::vector<DesignTools::TreeItem *> &items);
|
||||||
|
|
||||||
|
double m_minTime;
|
||||||
|
|
||||||
|
double m_maxTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
@@ -66,6 +66,7 @@ const char C_PLAY[] = "QmlDesigner.Play";
|
|||||||
const char C_NEXT[] = "QmlDesigner.Next";
|
const char C_NEXT[] = "QmlDesigner.Next";
|
||||||
const char C_AUTO_KEYFRAME[] = "QmlDesigner.AutoKeyframe";
|
const char C_AUTO_KEYFRAME[] = "QmlDesigner.AutoKeyframe";
|
||||||
const char C_CURVE_PICKER[] = "QmlDesigner.CurvePicker";
|
const char C_CURVE_PICKER[] = "QmlDesigner.CurvePicker";
|
||||||
|
const char C_CURVE_EDITOR[] = "QmlDesigner.CurveEditor";
|
||||||
const char C_ZOOM_IN[] = "QmlDesigner.ZoomIn";
|
const char C_ZOOM_IN[] = "QmlDesigner.ZoomIn";
|
||||||
const char C_ZOOM_OUT[] = "QmlDesigner.ZoomOut";
|
const char C_ZOOM_OUT[] = "QmlDesigner.ZoomOut";
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ SOURCES += \
|
|||||||
easingcurve.cpp \
|
easingcurve.cpp \
|
||||||
timelinesettingsmodel.cpp \
|
timelinesettingsmodel.cpp \
|
||||||
timelinetooldelegate.cpp \
|
timelinetooldelegate.cpp \
|
||||||
timelinecontrols.cpp
|
timelinecontrols.cpp \
|
||||||
|
animationcurveeditormodel.cpp \
|
||||||
|
animationcurvedialog.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
timelineview.h \
|
timelineview.h \
|
||||||
@@ -69,7 +71,9 @@ HEADERS += \
|
|||||||
canvasstyledialog.h \
|
canvasstyledialog.h \
|
||||||
easingcurve.h \
|
easingcurve.h \
|
||||||
timelinesettingsmodel.h \
|
timelinesettingsmodel.h \
|
||||||
timelinecontrols.h
|
timelinecontrols.h \
|
||||||
|
animationcurveeditormodel.h \
|
||||||
|
animationcurvedialog.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
timeline.qrc
|
timeline.qrc
|
||||||
|
|||||||
@@ -44,11 +44,13 @@
|
|||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QIntValidator>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
#include <QIntValidator>
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
@@ -99,7 +101,19 @@ QAction *createAction(const Core::Id &id,
|
|||||||
TimelineToolBar::TimelineToolBar(QWidget *parent)
|
TimelineToolBar::TimelineToolBar(QWidget *parent)
|
||||||
: QToolBar(parent)
|
: QToolBar(parent)
|
||||||
, m_grp()
|
, m_grp()
|
||||||
|
, m_dialog()
|
||||||
|
, m_curveModel(new AnimationCurveEditorModel(0., 500.))
|
||||||
{
|
{
|
||||||
|
m_dialog.setModel(m_curveModel);
|
||||||
|
connect(m_curveModel,
|
||||||
|
&AnimationCurveEditorModel::currentFrameChanged,
|
||||||
|
this,
|
||||||
|
&TimelineToolBar::currentFrameChanged);
|
||||||
|
connect(m_curveModel,
|
||||||
|
&AnimationCurveEditorModel::curveChanged,
|
||||||
|
this,
|
||||||
|
&TimelineToolBar::curveChanged);
|
||||||
|
|
||||||
setContentsMargins(0, 0, 0, 0);
|
setContentsMargins(0, 0, 0, 0);
|
||||||
createLeftControls();
|
createLeftControls();
|
||||||
createCenterControls();
|
createCenterControls();
|
||||||
@@ -146,6 +160,7 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline)
|
|||||||
setStartFrame(timeline.startKeyframe());
|
setStartFrame(timeline.startKeyframe());
|
||||||
setEndFrame(timeline.endKeyframe());
|
setEndFrame(timeline.endKeyframe());
|
||||||
m_timelineLabel->setText(timeline.modelNode().id());
|
m_timelineLabel->setText(timeline.modelNode().id());
|
||||||
|
m_curveModel->setTimeline(timeline);
|
||||||
} else {
|
} else {
|
||||||
m_timelineLabel->setText("");
|
m_timelineLabel->setText("");
|
||||||
}
|
}
|
||||||
@@ -153,6 +168,8 @@ void TimelineToolBar::setCurrentTimeline(const QmlTimeline &timeline)
|
|||||||
|
|
||||||
void TimelineToolBar::setStartFrame(qreal frame)
|
void TimelineToolBar::setStartFrame(qreal frame)
|
||||||
{
|
{
|
||||||
|
m_curveModel->setMinimumTime(frame);
|
||||||
|
|
||||||
auto text = QString::number(frame, 'f', 0);
|
auto text = QString::number(frame, 'f', 0);
|
||||||
m_firstFrame->setText(text);
|
m_firstFrame->setText(text);
|
||||||
setupCurrentFrameValidator();
|
setupCurrentFrameValidator();
|
||||||
@@ -160,12 +177,16 @@ void TimelineToolBar::setStartFrame(qreal frame)
|
|||||||
|
|
||||||
void TimelineToolBar::setCurrentFrame(qreal frame)
|
void TimelineToolBar::setCurrentFrame(qreal frame)
|
||||||
{
|
{
|
||||||
|
m_curveModel->setCurrentFrame(std::round(frame));
|
||||||
|
|
||||||
auto text = QString::number(frame, 'f', 0);
|
auto text = QString::number(frame, 'f', 0);
|
||||||
m_currentFrame->setText(text);
|
m_currentFrame->setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineToolBar::setEndFrame(qreal frame)
|
void TimelineToolBar::setEndFrame(qreal frame)
|
||||||
{
|
{
|
||||||
|
m_curveModel->setMaximumTime(frame);
|
||||||
|
|
||||||
auto text = QString::number(frame, 'f', 0);
|
auto text = QString::number(frame, 'f', 0);
|
||||||
m_lastFrame->setText(text);
|
m_lastFrame->setText(text);
|
||||||
setupCurrentFrameValidator();
|
setupCurrentFrameValidator();
|
||||||
@@ -190,6 +211,16 @@ void TimelineToolBar::removeTimeline(const QmlTimeline &timeline)
|
|||||||
setCurrentTimeline(QmlTimeline());
|
setCurrentTimeline(QmlTimeline());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TimelineToolBar::openAnimationCurveEditor()
|
||||||
|
{
|
||||||
|
m_dialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineToolBar::updateCurve(DesignTools::PropertyTreeItem *item)
|
||||||
|
{
|
||||||
|
DesignTools::AnimationCurve curve = item->curve();
|
||||||
|
}
|
||||||
|
|
||||||
void TimelineToolBar::createLeftControls()
|
void TimelineToolBar::createLeftControls()
|
||||||
{
|
{
|
||||||
auto addActionToGroup = [&](QAction *action) {
|
auto addActionToGroup = [&](QAction *action) {
|
||||||
@@ -217,9 +248,19 @@ void TimelineToolBar::createLeftControls()
|
|||||||
QKeySequence(Qt::Key_S));
|
QKeySequence(Qt::Key_S));
|
||||||
|
|
||||||
connect(settingsAction, &QAction::triggered, this, &TimelineToolBar::settingDialogClicked);
|
connect(settingsAction, &QAction::triggered, this, &TimelineToolBar::settingDialogClicked);
|
||||||
|
|
||||||
addActionToGroup(settingsAction);
|
addActionToGroup(settingsAction);
|
||||||
|
|
||||||
|
auto *curveEditorAction = createAction(TimelineConstants::C_CURVE_EDITOR,
|
||||||
|
TimelineIcons::ANIMATION.icon(),
|
||||||
|
tr("Curve Editor"),
|
||||||
|
QKeySequence(Qt::Key_C));
|
||||||
|
|
||||||
|
connect(curveEditorAction,
|
||||||
|
&QAction::triggered,
|
||||||
|
this,
|
||||||
|
&TimelineToolBar::openAnimationCurveEditor);
|
||||||
|
addActionToGroup(curveEditorAction);
|
||||||
|
|
||||||
addWidgetToGroup(createSpacer());
|
addWidgetToGroup(createSpacer());
|
||||||
|
|
||||||
m_timelineLabel = new QLabel(this);
|
m_timelineLabel = new QLabel(this);
|
||||||
@@ -434,8 +475,9 @@ void TimelineToolBar::addSpacing(int width)
|
|||||||
|
|
||||||
void TimelineToolBar::setupCurrentFrameValidator()
|
void TimelineToolBar::setupCurrentFrameValidator()
|
||||||
{
|
{
|
||||||
auto validator = static_cast<const QIntValidator*>(m_currentFrame->validator());
|
auto validator = static_cast<const QIntValidator *>(m_currentFrame->validator());
|
||||||
const_cast<QIntValidator*>(validator)->setRange(m_firstFrame->text().toInt(), m_lastFrame->text().toInt());
|
const_cast<QIntValidator *>(validator)->setRange(m_firstFrame->text().toInt(),
|
||||||
|
m_lastFrame->text().toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineToolBar::resizeEvent(QResizeEvent *event)
|
void TimelineToolBar::resizeEvent(QResizeEvent *event)
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "animationcurvedialog.h"
|
||||||
|
#include "animationcurveeditormodel.h"
|
||||||
|
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
|
||||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||||
@@ -46,7 +49,7 @@ class TimelineToolBar : public QToolBar
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void settingDialogClicked();
|
void settingDialogClicked();
|
||||||
//void addTimelineClicked();
|
void curveEditorDialogClicked();
|
||||||
|
|
||||||
void openEasingCurveEditor();
|
void openEasingCurveEditor();
|
||||||
|
|
||||||
@@ -64,6 +67,8 @@ signals:
|
|||||||
void currentFrameChanged(int value);
|
void currentFrameChanged(int value);
|
||||||
void endFrameChanged(int value);
|
void endFrameChanged(int value);
|
||||||
|
|
||||||
|
void curveChanged(DesignTools::PropertyTreeItem *item);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TimelineToolBar(QWidget *parent = nullptr);
|
explicit TimelineToolBar(QWidget *parent = nullptr);
|
||||||
|
|
||||||
@@ -83,6 +88,10 @@ public:
|
|||||||
void setActionEnabled(const QString &name, bool enabled);
|
void setActionEnabled(const QString &name, bool enabled);
|
||||||
void removeTimeline(const QmlTimeline &timeline);
|
void removeTimeline(const QmlTimeline &timeline);
|
||||||
|
|
||||||
|
void openAnimationCurveEditor();
|
||||||
|
|
||||||
|
void updateCurve(DesignTools::PropertyTreeItem *item);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
@@ -95,6 +104,10 @@ private:
|
|||||||
|
|
||||||
QList<QObject *> m_grp;
|
QList<QObject *> m_grp;
|
||||||
|
|
||||||
|
AnimationCurveDialog m_dialog;
|
||||||
|
|
||||||
|
AnimationCurveEditorModel *m_curveModel = nullptr;
|
||||||
|
|
||||||
QLabel *m_timelineLabel = nullptr;
|
QLabel *m_timelineLabel = nullptr;
|
||||||
QLabel *m_stateLabel = nullptr;
|
QLabel *m_stateLabel = nullptr;
|
||||||
QSlider *m_scale = nullptr;
|
QSlider *m_scale = nullptr;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
#include <nodemetainfo.h>
|
#include <nodemetainfo.h>
|
||||||
#include <rewritertransaction.h>
|
#include <rewritertransaction.h>
|
||||||
#include <variantproperty.h>
|
#include <variantproperty.h>
|
||||||
|
#include <viewmanager.h>
|
||||||
#include <qmldesignericons.h>
|
#include <qmldesignericons.h>
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
#include <qmlitemnode.h>
|
#include <qmlitemnode.h>
|
||||||
@@ -50,7 +51,6 @@
|
|||||||
#include <qmlstate.h>
|
#include <qmlstate.h>
|
||||||
#include <qmltimeline.h>
|
#include <qmltimeline.h>
|
||||||
#include <qmltimelinekeyframegroup.h>
|
#include <qmltimelinekeyframegroup.h>
|
||||||
#include <viewmanager.h>
|
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
TimelineView::TimelineView(QObject *parent)
|
TimelineView::TimelineView(QObject *parent)
|
||||||
: AbstractView(parent)
|
: AbstractView(parent)
|
||||||
|
, m_timelineWidget(nullptr)
|
||||||
{
|
{
|
||||||
EasingCurve::registerStreamOperators();
|
EasingCurve::registerStreamOperators();
|
||||||
}
|
}
|
||||||
@@ -106,7 +107,8 @@ void TimelineView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
|||||||
if (lastId != currentId)
|
if (lastId != currentId)
|
||||||
m_timelineWidget->setTimelineId(currentId);
|
m_timelineWidget->setTimelineId(currentId);
|
||||||
} else if (removedNode.parentProperty().isValid()
|
} else if (removedNode.parentProperty().isValid()
|
||||||
&& QmlTimeline::isValidQmlTimeline(removedNode.parentProperty().parentModelNode())) {
|
&& QmlTimeline::isValidQmlTimeline(
|
||||||
|
removedNode.parentProperty().parentModelNode())) {
|
||||||
if (removedNode.hasBindingProperty("target")) {
|
if (removedNode.hasBindingProperty("target")) {
|
||||||
const ModelNode target = removedNode.bindingProperty("target").resolveToModelNode();
|
const ModelNode target = removedNode.bindingProperty("target").resolveToModelNode();
|
||||||
if (target.isValid()) {
|
if (target.isValid()) {
|
||||||
@@ -114,7 +116,8 @@ void TimelineView::nodeAboutToBeRemoved(const ModelNode &removedNode)
|
|||||||
if (timeline.hasKeyframeGroupForTarget(target))
|
if (timeline.hasKeyframeGroupForTarget(target))
|
||||||
QTimer::singleShot(0, [this, target, timeline]() {
|
QTimer::singleShot(0, [this, target, timeline]() {
|
||||||
if (timeline.hasKeyframeGroupForTarget(target))
|
if (timeline.hasKeyframeGroupForTarget(target))
|
||||||
m_timelineWidget->graphicsScene()->invalidateSectionForTarget(target);
|
m_timelineWidget->graphicsScene()->invalidateSectionForTarget(
|
||||||
|
target);
|
||||||
else
|
else
|
||||||
m_timelineWidget->graphicsScene()->invalidateScene();
|
m_timelineWidget->graphicsScene()->invalidateScene();
|
||||||
});
|
});
|
||||||
@@ -186,6 +189,9 @@ void TimelineView::variantPropertiesChanged(const QList<VariantProperty> &proper
|
|||||||
if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode)) {
|
if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode)) {
|
||||||
QmlTimelineKeyframeGroup frames(framesNode);
|
QmlTimelineKeyframeGroup frames(framesNode);
|
||||||
m_timelineWidget->graphicsScene()->invalidateKeyframesForTarget(frames.target());
|
m_timelineWidget->graphicsScene()->invalidateKeyframesForTarget(frames.target());
|
||||||
|
|
||||||
|
QmlTimeline currentTimeline = m_timelineWidget->graphicsScene()->currentTimeline();
|
||||||
|
m_timelineWidget->toolBar()->setCurrentTimeline(currentTimeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +270,7 @@ const QmlTimeline TimelineView::addNewTimeline()
|
|||||||
|
|
||||||
ModelNode timelineNode;
|
ModelNode timelineNode;
|
||||||
|
|
||||||
executeInTransaction("TimelineView::addNewTimeline", [=, &timelineNode](){
|
executeInTransaction("TimelineView::addNewTimeline", [=, &timelineNode]() {
|
||||||
bool hasTimelines = getTimelines().isEmpty();
|
bool hasTimelines = getTimelines().isEmpty();
|
||||||
|
|
||||||
timelineNode = createModelNode(timelineType,
|
timelineNode = createModelNode(timelineType,
|
||||||
@@ -296,7 +302,7 @@ ModelNode TimelineView::addAnimation(QmlTimeline timeline)
|
|||||||
|
|
||||||
ModelNode animationNode;
|
ModelNode animationNode;
|
||||||
|
|
||||||
executeInTransaction("TimelineView::addAnimation", [=, &animationNode](){
|
executeInTransaction("TimelineView::addAnimation", [=, &animationNode]() {
|
||||||
animationNode = createModelNode(animationType,
|
animationNode = createModelNode(animationType,
|
||||||
metaInfo.majorVersion(),
|
metaInfo.majorVersion(),
|
||||||
metaInfo.minorVersion());
|
metaInfo.minorVersion());
|
||||||
@@ -380,22 +386,20 @@ void TimelineView::insertKeyframe(const ModelNode &target, const PropertyName &p
|
|||||||
QmlTimeline timeline = widget()->graphicsScene()->currentTimeline();
|
QmlTimeline timeline = widget()->graphicsScene()->currentTimeline();
|
||||||
ModelNode targetNode = target;
|
ModelNode targetNode = target;
|
||||||
if (timeline.isValid() && targetNode.isValid()
|
if (timeline.isValid() && targetNode.isValid()
|
||||||
&& QmlObjectNode::isValidQmlObjectNode(targetNode)) {
|
&& QmlObjectNode::isValidQmlObjectNode(targetNode)) {
|
||||||
executeInTransaction("TimelineView::insertKeyframe", [=, &timeline, &targetNode](){
|
executeInTransaction("TimelineView::insertKeyframe", [=, &timeline, &targetNode]() {
|
||||||
|
|
||||||
targetNode.validId();
|
targetNode.validId();
|
||||||
|
|
||||||
QmlTimelineKeyframeGroup timelineFrames(
|
QmlTimelineKeyframeGroup timelineFrames(
|
||||||
timeline.keyframeGroup(targetNode, propertyName));
|
timeline.keyframeGroup(targetNode, propertyName));
|
||||||
|
|
||||||
QTC_ASSERT(timelineFrames.isValid(), return );
|
QTC_ASSERT(timelineFrames.isValid(), return );
|
||||||
|
|
||||||
const qreal frame
|
const qreal frame
|
||||||
= timeline.modelNode().auxiliaryData("currentFrame@NodeInstance").toReal();
|
= timeline.modelNode().auxiliaryData("currentFrame@NodeInstance").toReal();
|
||||||
const QVariant value = QmlObjectNode(targetNode).instanceValue(propertyName);
|
const QVariant value = QmlObjectNode(targetNode).instanceValue(propertyName);
|
||||||
|
|
||||||
timelineFrames.setValue(value, frame);
|
timelineFrames.setValue(value, frame);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,7 +412,8 @@ QList<QmlTimeline> TimelineView::getTimelines() const
|
|||||||
return timelines;
|
return timelines;
|
||||||
|
|
||||||
for (const ModelNode &modelNode : allModelNodes()) {
|
for (const ModelNode &modelNode : allModelNodes()) {
|
||||||
if (QmlTimeline::isValidQmlTimeline(modelNode) && !modelNode.hasAuxiliaryData("removed@Internal")) {
|
if (QmlTimeline::isValidQmlTimeline(modelNode)
|
||||||
|
&& !modelNode.hasAuxiliaryData("removed@Internal")) {
|
||||||
timelines.append(modelNode);
|
timelines.append(modelNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,11 +487,10 @@ void TimelineView::registerActions()
|
|||||||
|
|
||||||
SelectionContextPredicate timelineEnabled = [this](const SelectionContext &context) {
|
SelectionContextPredicate timelineEnabled = [this](const SelectionContext &context) {
|
||||||
return context.singleNodeIsSelected()
|
return context.singleNodeIsSelected()
|
||||||
&& widget()->graphicsScene()->currentTimeline().isValid();
|
&& widget()->graphicsScene()->currentTimeline().isValid();
|
||||||
};
|
};
|
||||||
|
|
||||||
SelectionContextPredicate timelineHasKeyframes =
|
SelectionContextPredicate timelineHasKeyframes = [this](const SelectionContext &context) {
|
||||||
[this](const SelectionContext &context) {
|
|
||||||
auto timeline = widget()->graphicsScene()->currentTimeline();
|
auto timeline = widget()->graphicsScene()->currentTimeline();
|
||||||
return !timeline.keyframeGroupsForTarget(context.currentSingleSelectedNode()).isEmpty();
|
return !timeline.keyframeGroupsForTarget(context.currentSingleSelectedNode()).isEmpty();
|
||||||
};
|
};
|
||||||
@@ -528,44 +532,44 @@ void TimelineView::registerActions()
|
|||||||
&SelectionContextFunctors::always));
|
&SelectionContextFunctors::always));
|
||||||
|
|
||||||
actionManager.addDesignerAction(
|
actionManager.addDesignerAction(
|
||||||
new ModelNodeContextMenuAction("commandId timeline delete",
|
new ModelNodeContextMenuAction("commandId timeline delete",
|
||||||
TimelineConstants::timelineDeleteKeyframesDisplayName,
|
TimelineConstants::timelineDeleteKeyframesDisplayName,
|
||||||
{},
|
{},
|
||||||
TimelineConstants::timelineCategory,
|
TimelineConstants::timelineCategory,
|
||||||
QKeySequence(),
|
QKeySequence(),
|
||||||
160,
|
160,
|
||||||
deleteKeyframes,
|
deleteKeyframes,
|
||||||
timelineHasKeyframes));
|
timelineHasKeyframes));
|
||||||
|
|
||||||
actionManager.addDesignerAction(
|
actionManager.addDesignerAction(
|
||||||
new ModelNodeContextMenuAction("commandId timeline insert",
|
new ModelNodeContextMenuAction("commandId timeline insert",
|
||||||
TimelineConstants::timelineInsertKeyframesDisplayName,
|
TimelineConstants::timelineInsertKeyframesDisplayName,
|
||||||
{},
|
{},
|
||||||
TimelineConstants::timelineCategory,
|
TimelineConstants::timelineCategory,
|
||||||
QKeySequence(),
|
QKeySequence(),
|
||||||
140,
|
140,
|
||||||
insertKeyframes,
|
insertKeyframes,
|
||||||
timelineHasKeyframes));
|
timelineHasKeyframes));
|
||||||
|
|
||||||
actionManager.addDesignerAction(
|
actionManager.addDesignerAction(
|
||||||
new ModelNodeContextMenuAction("commandId timeline copy",
|
new ModelNodeContextMenuAction("commandId timeline copy",
|
||||||
TimelineConstants::timelineCopyKeyframesDisplayName,
|
TimelineConstants::timelineCopyKeyframesDisplayName,
|
||||||
{},
|
{},
|
||||||
TimelineConstants::timelineCategory,
|
TimelineConstants::timelineCategory,
|
||||||
QKeySequence(),
|
QKeySequence(),
|
||||||
120,
|
120,
|
||||||
copyKeyframes,
|
copyKeyframes,
|
||||||
timelineHasKeyframes));
|
timelineHasKeyframes));
|
||||||
|
|
||||||
actionManager.addDesignerAction(
|
actionManager.addDesignerAction(
|
||||||
new ModelNodeContextMenuAction("commandId timeline paste",
|
new ModelNodeContextMenuAction("commandId timeline paste",
|
||||||
TimelineConstants::timelinePasteKeyframesDisplayName,
|
TimelineConstants::timelinePasteKeyframesDisplayName,
|
||||||
{},
|
{},
|
||||||
TimelineConstants::timelineCategory,
|
TimelineConstants::timelineCategory,
|
||||||
QKeySequence(),
|
QKeySequence(),
|
||||||
100,
|
100,
|
||||||
pasteKeyframes,
|
pasteKeyframes,
|
||||||
timelineHasClipboard));
|
timelineHasClipboard));
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineWidget *TimelineView::createWidget()
|
TimelineWidget *TimelineView::createWidget()
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "animationcurvedialog.h"
|
||||||
|
#include "animationcurveeditormodel.h"
|
||||||
|
#include "treeitem.h"
|
||||||
|
|
||||||
#include <abstractview.h>
|
#include <abstractview.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "timelinewidget.h"
|
#include "timelinewidget.h"
|
||||||
|
#include "bindingproperty.h"
|
||||||
|
#include "curvesegment.h"
|
||||||
|
#include "easingcurve.h"
|
||||||
#include "easingcurvedialog.h"
|
#include "easingcurvedialog.h"
|
||||||
#include "timelineconstants.h"
|
#include "timelineconstants.h"
|
||||||
#include "timelinegraphicsscene.h"
|
#include "timelinegraphicsscene.h"
|
||||||
@@ -203,6 +206,8 @@ void TimelineWidget::connectToolbar()
|
|||||||
|
|
||||||
connect(graphicsScene(), &TimelineGraphicsScene::scroll, this, &TimelineWidget::scroll);
|
connect(graphicsScene(), &TimelineGraphicsScene::scroll, this, &TimelineWidget::scroll);
|
||||||
|
|
||||||
|
connect(m_toolbar, &TimelineToolBar::curveChanged, this, &TimelineWidget::updateAnimationCurve);
|
||||||
|
|
||||||
auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); };
|
auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); };
|
||||||
connect(m_toolbar, &TimelineToolBar::scaleFactorChanged, setRulerScaling);
|
connect(m_toolbar, &TimelineToolBar::scaleFactorChanged, setRulerScaling);
|
||||||
|
|
||||||
@@ -233,10 +238,7 @@ void TimelineWidget::connectToolbar()
|
|||||||
auto setEndFrame = [this](int end) { graphicsScene()->setEndFrame(end); };
|
auto setEndFrame = [this](int end) { graphicsScene()->setEndFrame(end); };
|
||||||
connect(m_toolbar, &TimelineToolBar::endFrameChanged, setEndFrame);
|
connect(m_toolbar, &TimelineToolBar::endFrameChanged, setEndFrame);
|
||||||
|
|
||||||
|
connect(m_toolbar, &TimelineToolBar::recordToggled, this, &TimelineWidget::setTimelineRecording);
|
||||||
connect(m_toolbar, &TimelineToolBar::recordToggled,
|
|
||||||
this,
|
|
||||||
&TimelineWidget::setTimelineRecording);
|
|
||||||
|
|
||||||
connect(m_toolbar,
|
connect(m_toolbar,
|
||||||
&TimelineToolBar::openEasingCurveEditor,
|
&TimelineToolBar::openEasingCurveEditor,
|
||||||
@@ -283,6 +285,79 @@ void TimelineWidget::scroll(const TimelineUtils::Side &side)
|
|||||||
m_scrollbar->setValue(m_scrollbar->value() + m_scrollbar->singleStep());
|
m_scrollbar->setValue(m_scrollbar->value() + m_scrollbar->singleStep());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode getTargetNode(DesignTools::PropertyTreeItem *item, const QmlTimeline &timeline)
|
||||||
|
{
|
||||||
|
if (const DesignTools::NodeTreeItem *nodeItem = item->parentNodeTreeItem()) {
|
||||||
|
QString targetId = nodeItem->name();
|
||||||
|
if (timeline.isValid()) {
|
||||||
|
for (auto &&target : timeline.allTargets()) {
|
||||||
|
if (target.displayName() == targetId)
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ModelNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlTimelineKeyframeGroup timelineKeyframeGroup(QmlTimeline &timeline,
|
||||||
|
DesignTools::PropertyTreeItem *item)
|
||||||
|
{
|
||||||
|
ModelNode node = getTargetNode(item, timeline);
|
||||||
|
if (node.isValid())
|
||||||
|
return timeline.keyframeGroup(node, item->name().toLatin1());
|
||||||
|
|
||||||
|
return QmlTimelineKeyframeGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void attachEasingCurve(double frame,
|
||||||
|
const QEasingCurve &curve,
|
||||||
|
const QmlTimelineKeyframeGroup &group)
|
||||||
|
{
|
||||||
|
ModelNode frameNode = group.keyframe(frame);
|
||||||
|
if (frameNode.isValid()) {
|
||||||
|
auto expression = EasingCurve(curve).toString();
|
||||||
|
frameNode.bindingProperty("easing.bezierCurve").setExpression(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineWidget::updateAnimationCurve(DesignTools::PropertyTreeItem *item)
|
||||||
|
{
|
||||||
|
QmlTimeline currentTimeline = graphicsScene()->currentTimeline();
|
||||||
|
QmlTimelineKeyframeGroup group = timelineKeyframeGroup(currentTimeline, item);
|
||||||
|
|
||||||
|
if (group.isValid()) {
|
||||||
|
auto replaceKeyframes = [&group, currentTimeline, item]() {
|
||||||
|
for (auto frame : group.keyframes())
|
||||||
|
frame.destroy();
|
||||||
|
|
||||||
|
DesignTools::Keyframe previous;
|
||||||
|
for (auto &&frame : item->curve().keyframes()) {
|
||||||
|
QPointF pos = frame.position();
|
||||||
|
group.setValue(QVariant(pos.y()), pos.x());
|
||||||
|
|
||||||
|
if (previous.isValid()) {
|
||||||
|
if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Bezier) {
|
||||||
|
DesignTools::CurveSegment segment(previous, frame);
|
||||||
|
attachEasingCurve(pos.x(), segment.easingCurve(), group);
|
||||||
|
} else if (frame.interpolation()
|
||||||
|
== DesignTools::Keyframe::Interpolation::Easing) {
|
||||||
|
QVariant data = frame.data();
|
||||||
|
if (data.type() == static_cast<int>(QMetaType::QEasingCurve))
|
||||||
|
attachEasingCurve(pos.x(), data.value<QEasingCurve>(), group);
|
||||||
|
} else if (frame.interpolation() == DesignTools::Keyframe::Interpolation::Step) {
|
||||||
|
// Warning: Keyframe::Interpolation::Step not yet implemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = frame;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
timelineView()->executeInTransaction("TimelineWidget::handleKeyframeReplacement",
|
||||||
|
replaceKeyframes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TimelineWidget::selectionChanged()
|
void TimelineWidget::selectionChanged()
|
||||||
{
|
{
|
||||||
if (graphicsScene()->hasSelection())
|
if (graphicsScene()->hasSelection())
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "animationcurveeditormodel.h"
|
||||||
#include "timelineutils.h"
|
#include "timelineutils.h"
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
|
|
||||||
@@ -77,6 +78,8 @@ public slots:
|
|||||||
void changeScaleFactor(int factor);
|
void changeScaleFactor(int factor);
|
||||||
void scroll(const TimelineUtils::Side &side);
|
void scroll(const TimelineUtils::Side &side);
|
||||||
|
|
||||||
|
void updateAnimationCurve(DesignTools::PropertyTreeItem *item);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qmldesignercorelib_global.h>
|
|
||||||
#include "qmlmodelnodefacade.h"
|
|
||||||
#include "qmlchangeset.h"
|
#include "qmlchangeset.h"
|
||||||
|
#include "qmlmodelnodefacade.h"
|
||||||
|
#include <qmldesignercorelib_global.h>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ class QmlTimeline;
|
|||||||
|
|
||||||
class QMLDESIGNERCORE_EXPORT QmlTimelineKeyframeGroup : public QmlModelNodeFacade
|
class QMLDESIGNERCORE_EXPORT QmlTimelineKeyframeGroup : public QmlModelNodeFacade
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QmlTimelineKeyframeGroup();
|
QmlTimelineKeyframeGroup();
|
||||||
QmlTimelineKeyframeGroup(const ModelNode &modelNode);
|
QmlTimelineKeyframeGroup(const ModelNode &modelNode);
|
||||||
@@ -64,6 +63,10 @@ public:
|
|||||||
qreal minActualKeyframe() const;
|
qreal minActualKeyframe() const;
|
||||||
qreal maxActualKeyframe() const;
|
qreal maxActualKeyframe() const;
|
||||||
|
|
||||||
|
ModelNode keyframe(qreal position) const;
|
||||||
|
|
||||||
|
const QList<ModelNode> keyframes() const;
|
||||||
|
|
||||||
const QList<ModelNode> keyframePositions() const;
|
const QList<ModelNode> keyframePositions() const;
|
||||||
|
|
||||||
static bool isValidKeyframe(const ModelNode &node);
|
static bool isValidKeyframe(const ModelNode &node);
|
||||||
@@ -83,4 +86,4 @@ public:
|
|||||||
QmlTimeline timeline() const;
|
QmlTimeline timeline() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|||||||
@@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
#include "qmltimelinekeyframegroup.h"
|
#include "qmltimelinekeyframegroup.h"
|
||||||
#include "abstractview.h"
|
#include "abstractview.h"
|
||||||
#include <nodelistproperty.h>
|
|
||||||
#include <variantproperty.h>
|
|
||||||
#include <metainfo.h>
|
|
||||||
#include <invalidmodelnodeexception.h>
|
|
||||||
#include "bindingproperty.h"
|
#include "bindingproperty.h"
|
||||||
#include "qmlitemnode.h"
|
#include "qmlitemnode.h"
|
||||||
|
#include <invalidmodelnodeexception.h>
|
||||||
|
#include <metainfo.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
#include <variantproperty.h>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
@@ -41,10 +41,9 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
QmlTimelineKeyframeGroup::QmlTimelineKeyframeGroup() = default;
|
QmlTimelineKeyframeGroup::QmlTimelineKeyframeGroup() = default;
|
||||||
|
|
||||||
QmlTimelineKeyframeGroup::QmlTimelineKeyframeGroup(const ModelNode &modelNode) : QmlModelNodeFacade(modelNode)
|
QmlTimelineKeyframeGroup::QmlTimelineKeyframeGroup(const ModelNode &modelNode)
|
||||||
{
|
: QmlModelNodeFacade(modelNode)
|
||||||
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
bool QmlTimelineKeyframeGroup::isValid() const
|
bool QmlTimelineKeyframeGroup::isValid() const
|
||||||
{
|
{
|
||||||
@@ -53,8 +52,8 @@ bool QmlTimelineKeyframeGroup::isValid() const
|
|||||||
|
|
||||||
bool QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(const ModelNode &modelNode)
|
bool QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(const ModelNode &modelNode)
|
||||||
{
|
{
|
||||||
return modelNode.isValid() && modelNode.metaInfo().isValid()
|
return modelNode.isValid() && modelNode.metaInfo().isValid()
|
||||||
&& modelNode.metaInfo().isSubclassOf("QtQuick.Timeline.KeyframeGroup");
|
&& modelNode.metaInfo().isSubclassOf("QtQuick.Timeline.KeyframeGroup");
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlTimelineKeyframeGroup::destroy()
|
void QmlTimelineKeyframeGroup::destroy()
|
||||||
@@ -73,12 +72,11 @@ ModelNode QmlTimelineKeyframeGroup::target() const
|
|||||||
|
|
||||||
void QmlTimelineKeyframeGroup::setTarget(const ModelNode &target)
|
void QmlTimelineKeyframeGroup::setTarget(const ModelNode &target)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return);
|
QTC_ASSERT(isValid(), return );
|
||||||
|
|
||||||
modelNode().bindingProperty("target").setExpression(target.id());
|
modelNode().bindingProperty("target").setExpression(target.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PropertyName QmlTimelineKeyframeGroup::propertyName() const
|
PropertyName QmlTimelineKeyframeGroup::propertyName() const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return {});
|
QTC_ASSERT(isValid(), return {});
|
||||||
@@ -88,7 +86,7 @@ PropertyName QmlTimelineKeyframeGroup::propertyName() const
|
|||||||
|
|
||||||
void QmlTimelineKeyframeGroup::setPropertyName(const PropertyName &propertyName)
|
void QmlTimelineKeyframeGroup::setPropertyName(const PropertyName &propertyName)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return);
|
QTC_ASSERT(isValid(), return );
|
||||||
|
|
||||||
modelNode().variantProperty("property").setValue(QString::fromUtf8(propertyName));
|
modelNode().variantProperty("property").setValue(QString::fromUtf8(propertyName));
|
||||||
}
|
}
|
||||||
@@ -135,7 +133,7 @@ bool QmlTimelineKeyframeGroup::isRecording() const
|
|||||||
|
|
||||||
void QmlTimelineKeyframeGroup::toogleRecording(bool record) const
|
void QmlTimelineKeyframeGroup::toogleRecording(bool record) const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return);
|
QTC_ASSERT(isValid(), return );
|
||||||
|
|
||||||
if (!record) {
|
if (!record) {
|
||||||
if (isRecording())
|
if (isRecording())
|
||||||
@@ -157,7 +155,7 @@ QmlTimeline QmlTimelineKeyframeGroup::timeline() const
|
|||||||
|
|
||||||
void QmlTimelineKeyframeGroup::setValue(const QVariant &value, qreal currentFrame)
|
void QmlTimelineKeyframeGroup::setValue(const QVariant &value, qreal currentFrame)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return);
|
QTC_ASSERT(isValid(), return );
|
||||||
|
|
||||||
for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) {
|
for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) {
|
||||||
if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), currentFrame)) {
|
if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), currentFrame)) {
|
||||||
@@ -166,10 +164,14 @@ void QmlTimelineKeyframeGroup::setValue(const QVariant &value, qreal currentFram
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<QPair<PropertyName, QVariant> > propertyPairList{{PropertyName("frame"), QVariant(currentFrame)},
|
const QList<QPair<PropertyName, QVariant>> propertyPairList{{PropertyName("frame"),
|
||||||
{PropertyName("value"), value}};
|
QVariant(currentFrame)},
|
||||||
|
{PropertyName("value"), value}};
|
||||||
|
|
||||||
ModelNode frame = modelNode().view()->createModelNode("QtQuick.Timeline.Keyframe", 1, 0, propertyPairList);
|
ModelNode frame = modelNode().view()->createModelNode("QtQuick.Timeline.Keyframe",
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
propertyPairList);
|
||||||
NodeListProperty nodeListProperty = modelNode().defaultNodeListProperty();
|
NodeListProperty nodeListProperty = modelNode().defaultNodeListProperty();
|
||||||
|
|
||||||
const int sourceIndex = nodeListProperty.count();
|
const int sourceIndex = nodeListProperty.count();
|
||||||
@@ -215,6 +217,16 @@ bool QmlTimelineKeyframeGroup::hasKeyframe(qreal frame)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode QmlTimelineKeyframeGroup::keyframe(qreal frame) const
|
||||||
|
{
|
||||||
|
for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) {
|
||||||
|
if (qFuzzyCompare(childNode.variantProperty("frame").value().toReal(), frame))
|
||||||
|
return childNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelNode();
|
||||||
|
}
|
||||||
|
|
||||||
qreal QmlTimelineKeyframeGroup::minActualKeyframe() const
|
qreal QmlTimelineKeyframeGroup::minActualKeyframe() const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(isValid(), return -1);
|
QTC_ASSERT(isValid(), return -1);
|
||||||
@@ -243,6 +255,11 @@ qreal QmlTimelineKeyframeGroup::maxActualKeyframe() const
|
|||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<ModelNode> QmlTimelineKeyframeGroup::keyframes() const
|
||||||
|
{
|
||||||
|
return modelNode().defaultNodeListProperty().toModelNodeList();
|
||||||
|
}
|
||||||
|
|
||||||
const QList<ModelNode> QmlTimelineKeyframeGroup::keyframePositions() const
|
const QList<ModelNode> QmlTimelineKeyframeGroup::keyframePositions() const
|
||||||
{
|
{
|
||||||
QList<ModelNode> returnValues;
|
QList<ModelNode> returnValues;
|
||||||
@@ -257,14 +274,13 @@ const QList<ModelNode> QmlTimelineKeyframeGroup::keyframePositions() const
|
|||||||
|
|
||||||
bool QmlTimelineKeyframeGroup::isValidKeyframe(const ModelNode &node)
|
bool QmlTimelineKeyframeGroup::isValidKeyframe(const ModelNode &node)
|
||||||
{
|
{
|
||||||
return isValidQmlModelNodeFacade(node)
|
return isValidQmlModelNodeFacade(node) && node.metaInfo().isValid()
|
||||||
&& node.metaInfo().isValid()
|
&& node.metaInfo().isSubclassOf("QtQuick.Timeline.Keyframe");
|
||||||
&& node.metaInfo().isSubclassOf("QtQuick.Timeline.Keyframe");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QmlTimelineKeyframeGroup::checkKeyframesType(const ModelNode &node)
|
bool QmlTimelineKeyframeGroup::checkKeyframesType(const ModelNode &node)
|
||||||
{
|
{
|
||||||
return node.isValid() && node.type() == "QtQuick.Timeline.KeyframeGroup";
|
return node.isValid() && node.type() == "QtQuick.Timeline.KeyframeGroup";
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlTimelineKeyframeGroup QmlTimelineKeyframeGroup::keyframeGroupForKeyframe(const ModelNode &node)
|
QmlTimelineKeyframeGroup QmlTimelineKeyframeGroup::keyframeGroupForKeyframe(const ModelNode &node)
|
||||||
@@ -297,4 +313,4 @@ void QmlTimelineKeyframeGroup::scaleAllKeyframes(qreal factor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|||||||
Reference in New Issue
Block a user