forked from qt-creator/qt-creator
QmlDesigner: Improve usability of boolean animation curves
Boolean animation curves are now painted from min to max of the value range instead of from 0 to 1. This allows to see value changes when the curve is loaded together with other animation curves of different value ranges. Boolean curves are now forced to be step interpolated. If the user tries other interpolation types the request is denied and an error message is written in the new status line. Added a status line that displays the current frame number by default and an informative text in case the user did something forbidden. Respect the current state when populating the curve editor. Fixes: QDS-6950 Fixes: QDS-6889 Change-Id: Ia5fa1c1c55ee93eda5a39fd83987b54fb41d54db Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -36,14 +36,16 @@
|
|||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
AnimationCurve::AnimationCurve()
|
AnimationCurve::AnimationCurve()
|
||||||
: m_fromData(false)
|
: m_type(AnimationCurve::ValueType::Undefined)
|
||||||
|
, 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()
|
, m_frames()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
|
AnimationCurve::AnimationCurve(AnimationCurve::ValueType type, const std::vector<Keyframe> &frames)
|
||||||
: m_fromData(false)
|
: m_type(type)
|
||||||
|
, 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)
|
, m_frames(frames)
|
||||||
@@ -51,8 +53,13 @@ AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
|
|||||||
analyze();
|
analyze();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationCurve::AnimationCurve(const QEasingCurve &easing, const QPointF &start, const QPointF &end)
|
AnimationCurve::AnimationCurve(
|
||||||
: m_fromData(true)
|
AnimationCurve::ValueType type,
|
||||||
|
const QEasingCurve &easing,
|
||||||
|
const QPointF &start,
|
||||||
|
const QPointF &end)
|
||||||
|
: m_type(type)
|
||||||
|
, m_fromData(true)
|
||||||
, 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()
|
, m_frames()
|
||||||
@@ -117,6 +124,11 @@ bool AnimationCurve::hasUnified() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimationCurve::ValueType AnimationCurve::valueType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
double AnimationCurve::minimumTime() const
|
double AnimationCurve::minimumTime() const
|
||||||
{
|
{
|
||||||
if (!m_frames.empty())
|
if (!m_frames.empty())
|
||||||
|
|||||||
@@ -40,11 +40,17 @@ class CurveSegment;
|
|||||||
class AnimationCurve
|
class AnimationCurve
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using ValueType = Keyframe::ValueType;
|
||||||
|
|
||||||
AnimationCurve();
|
AnimationCurve();
|
||||||
|
|
||||||
AnimationCurve(const std::vector<Keyframe> &frames);
|
AnimationCurve(ValueType type, const std::vector<Keyframe> &frames);
|
||||||
|
|
||||||
AnimationCurve(const QEasingCurve &easing, const QPointF &start, const QPointF &end);
|
AnimationCurve(
|
||||||
|
ValueType type,
|
||||||
|
const QEasingCurve &easing,
|
||||||
|
const QPointF &start,
|
||||||
|
const QPointF &end);
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
|
|
||||||
@@ -54,6 +60,8 @@ public:
|
|||||||
|
|
||||||
bool hasUnified() const;
|
bool hasUnified() const;
|
||||||
|
|
||||||
|
ValueType valueType() const;
|
||||||
|
|
||||||
double minimumTime() const;
|
double minimumTime() const;
|
||||||
|
|
||||||
double maximumTime() const;
|
double maximumTime() const;
|
||||||
@@ -93,6 +101,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
void analyze();
|
void analyze();
|
||||||
|
|
||||||
|
ValueType m_type;
|
||||||
|
|
||||||
bool m_fromData;
|
bool m_fromData;
|
||||||
|
|
||||||
double m_minY;
|
double m_minY;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace QmlDesigner {
|
|||||||
CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_infoText(nullptr)
|
, m_infoText(nullptr)
|
||||||
|
, m_statusLine(nullptr)
|
||||||
, m_toolbar(new CurveEditorToolBar(model, this))
|
, m_toolbar(new CurveEditorToolBar(model, this))
|
||||||
, m_tree(new TreeView(model, this))
|
, m_tree(new TreeView(model, this))
|
||||||
, m_view(new GraphicsView(model, this))
|
, m_view(new GraphicsView(model, this))
|
||||||
@@ -61,10 +62,13 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
|||||||
area->setWidget(splitter);
|
area->setWidget(splitter);
|
||||||
area->setWidgetResizable(true);
|
area->setWidgetResizable(true);
|
||||||
|
|
||||||
|
m_statusLine = new QLabel();
|
||||||
|
|
||||||
auto *box = new QVBoxLayout;
|
auto *box = new QVBoxLayout;
|
||||||
box->addWidget(m_infoText);
|
box->addWidget(m_infoText);
|
||||||
box->addWidget(m_toolbar);
|
box->addWidget(m_toolbar);
|
||||||
box->addWidget(area);
|
box->addWidget(area);
|
||||||
|
box->addWidget(m_statusLine);
|
||||||
setLayout(box);
|
setLayout(box);
|
||||||
|
|
||||||
connect(m_toolbar, &CurveEditorToolBar::defaultClicked, [this]() {
|
connect(m_toolbar, &CurveEditorToolBar::defaultClicked, [this]() {
|
||||||
@@ -89,9 +93,11 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
|||||||
m_view->viewport()->update();
|
m_view->viewport()->update();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(
|
connect(m_toolbar, &CurveEditorToolBar::currentFrameChanged, [this, model](int frame) {
|
||||||
m_toolbar, &CurveEditorToolBar::currentFrameChanged,
|
model->setCurrentFrame(frame);
|
||||||
model, &CurveEditorModel::commitCurrentFrame);
|
updateStatusLine();
|
||||||
|
m_view->viewport()->update();
|
||||||
|
});
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
m_view, &GraphicsView::currentFrameChanged,
|
m_view, &GraphicsView::currentFrameChanged,
|
||||||
@@ -106,6 +112,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
|||||||
|
|
||||||
auto updateTimeline = [this, model](bool validTimeline) {
|
auto updateTimeline = [this, model](bool validTimeline) {
|
||||||
if (validTimeline) {
|
if (validTimeline) {
|
||||||
|
updateStatusLine();
|
||||||
|
m_view->setCurrentFrame(m_view->model()->currentFrame(), false);
|
||||||
m_toolbar->updateBoundsSilent(model->minimumTime(), model->maximumTime());
|
m_toolbar->updateBoundsSilent(model->minimumTime(), model->maximumTime());
|
||||||
m_toolbar->show();
|
m_toolbar->show();
|
||||||
m_tree->show();
|
m_tree->show();
|
||||||
@@ -119,6 +127,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
connect(model, &CurveEditorModel::timelineChanged, this, updateTimeline);
|
connect(model, &CurveEditorModel::timelineChanged, this, updateTimeline);
|
||||||
|
|
||||||
|
connect(model, &CurveEditorModel::setStatusLineMsg, m_statusLine, &QLabel::setText);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CurveEditor::dragging() const
|
bool CurveEditor::dragging() const
|
||||||
@@ -153,4 +163,11 @@ void CurveEditor::hideEvent(QHideEvent *event)
|
|||||||
QWidget::hideEvent(event);
|
QWidget::hideEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CurveEditor::updateStatusLine()
|
||||||
|
{
|
||||||
|
int currentFrame = m_view->model()->currentFrame();
|
||||||
|
QString currentText = QString("Playhead frame %1").arg(currentFrame);
|
||||||
|
m_statusLine->setText(currentText);
|
||||||
|
}
|
||||||
|
|
||||||
} // End namespace QmlDesigner.
|
} // End namespace QmlDesigner.
|
||||||
|
|||||||
@@ -59,8 +59,12 @@ protected:
|
|||||||
void hideEvent(QHideEvent *event) override;
|
void hideEvent(QHideEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateStatusLine();
|
||||||
|
|
||||||
QLabel *m_infoText;
|
QLabel *m_infoText;
|
||||||
|
|
||||||
|
QLabel *m_statusLine;
|
||||||
|
|
||||||
CurveEditorToolBar *m_toolbar;
|
CurveEditorToolBar *m_toolbar;
|
||||||
|
|
||||||
TreeView *m_tree;
|
TreeView *m_tree;
|
||||||
|
|||||||
@@ -44,12 +44,18 @@ namespace QmlDesigner {
|
|||||||
CurveEditorModel::CurveEditorModel(QObject *parent)
|
CurveEditorModel::CurveEditorModel(QObject *parent)
|
||||||
: TreeModel(parent)
|
: TreeModel(parent)
|
||||||
, m_hasTimeline(false)
|
, m_hasTimeline(false)
|
||||||
|
, m_currentFrame(0)
|
||||||
, m_minTime(CurveEditorStyle::defaultTimeMin)
|
, m_minTime(CurveEditorStyle::defaultTimeMin)
|
||||||
, m_maxTime(CurveEditorStyle::defaultTimeMax)
|
, m_maxTime(CurveEditorStyle::defaultTimeMax)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CurveEditorModel::~CurveEditorModel() {}
|
CurveEditorModel::~CurveEditorModel() {}
|
||||||
|
|
||||||
|
int CurveEditorModel::currentFrame() const
|
||||||
|
{
|
||||||
|
return m_currentFrame;
|
||||||
|
}
|
||||||
|
|
||||||
double CurveEditorModel::minimumTime() const
|
double CurveEditorModel::minimumTime() const
|
||||||
{
|
{
|
||||||
return m_minTime;
|
return m_minTime;
|
||||||
@@ -102,6 +108,7 @@ void CurveEditorModel::setTimeline(const QmlDesigner::QmlTimeline &timeline)
|
|||||||
m_hasTimeline = timeline.isValid();
|
m_hasTimeline = timeline.isValid();
|
||||||
|
|
||||||
if (m_hasTimeline) {
|
if (m_hasTimeline) {
|
||||||
|
m_currentFrame = static_cast<int>(timeline.currentKeyframe());
|
||||||
m_minTime = timeline.startKeyframe();
|
m_minTime = timeline.startKeyframe();
|
||||||
m_maxTime = timeline.endKeyframe();
|
m_maxTime = timeline.endKeyframe();
|
||||||
std::vector<TreeItem *> items;
|
std::vector<TreeItem *> items;
|
||||||
@@ -116,8 +123,8 @@ void CurveEditorModel::setTimeline(const QmlDesigner::QmlTimeline &timeline)
|
|||||||
|
|
||||||
void CurveEditorModel::setCurrentFrame(int frame)
|
void CurveEditorModel::setCurrentFrame(int frame)
|
||||||
{
|
{
|
||||||
if (graphicsView())
|
m_currentFrame = frame;
|
||||||
graphicsView()->setCurrentFrame(frame, false);
|
emit commitCurrentFrame(m_currentFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveEditorModel::setMinimumTime(double time)
|
void CurveEditorModel::setMinimumTime(double time)
|
||||||
@@ -262,7 +269,7 @@ TreeItem *CurveEditorModel::createTopLevelItem(const QmlDesigner::QmlTimeline &t
|
|||||||
AnimationCurve curve = createAnimationCurve(grp);
|
AnimationCurve curve = createAnimationCurve(grp);
|
||||||
if (!curve.isEmpty()) {
|
if (!curve.isEmpty()) {
|
||||||
QString name = QString::fromUtf8(grp.propertyName());
|
QString name = QString::fromUtf8(grp.propertyName());
|
||||||
auto propertyItem = new PropertyTreeItem(name, curve, typeFrom(grp));
|
auto propertyItem = new PropertyTreeItem(name, curve);
|
||||||
|
|
||||||
QmlDesigner::ModelNode target = grp.modelNode();
|
QmlDesigner::ModelNode target = grp.modelNode();
|
||||||
if (target.hasAuxiliaryData("locked"))
|
if (target.hasAuxiliaryData("locked"))
|
||||||
@@ -288,7 +295,7 @@ AnimationCurve CurveEditorModel::createAnimationCurve(const QmlDesigner::QmlTime
|
|||||||
{
|
{
|
||||||
switch (typeFrom(group)) {
|
switch (typeFrom(group)) {
|
||||||
case PropertyTreeItem::ValueType::Bool:
|
case PropertyTreeItem::ValueType::Bool:
|
||||||
return createDoubleCurve(group);
|
return createBooleanCurve(group);
|
||||||
|
|
||||||
case PropertyTreeItem::ValueType::Integer:
|
case PropertyTreeItem::ValueType::Integer:
|
||||||
return createDoubleCurve(group);
|
return createDoubleCurve(group);
|
||||||
@@ -346,7 +353,13 @@ std::vector<Keyframe> resolveSmallCurves(const std::vector<Keyframe> &frames)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
AnimationCurve acurve(curve, previous.position(), frame.position());
|
// This is just a temporary curve. ValueType does not matter
|
||||||
|
AnimationCurve acurve(
|
||||||
|
AnimationCurve::ValueType::Undefined,
|
||||||
|
curve,
|
||||||
|
previous.position(),
|
||||||
|
frame.position());
|
||||||
|
|
||||||
previous.setRightHandle(acurve.keyframeAt(0).rightHandle());
|
previous.setRightHandle(acurve.keyframeAt(0).rightHandle());
|
||||||
out.push_back(acurve.keyframeAt(1));
|
out.push_back(acurve.keyframeAt(1));
|
||||||
continue;
|
continue;
|
||||||
@@ -357,6 +370,17 @@ std::vector<Keyframe> resolveSmallCurves(const std::vector<Keyframe> &frames)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AnimationCurve CurveEditorModel::createBooleanCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group)
|
||||||
|
{
|
||||||
|
std::vector<Keyframe> keyframes = createKeyframes(group.keyframePositions());
|
||||||
|
|
||||||
|
for (auto& keyframe : keyframes)
|
||||||
|
keyframe.setInterpolation(Keyframe::Interpolation::Step);
|
||||||
|
|
||||||
|
return AnimationCurve(typeFrom(group), keyframes);
|
||||||
|
}
|
||||||
|
|
||||||
AnimationCurve CurveEditorModel::createDoubleCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group)
|
AnimationCurve CurveEditorModel::createDoubleCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group)
|
||||||
{
|
{
|
||||||
std::vector<Keyframe> keyframes = createKeyframes(group.keyframePositions());
|
std::vector<Keyframe> keyframes = createKeyframes(group.keyframePositions());
|
||||||
@@ -374,7 +398,7 @@ AnimationCurve CurveEditorModel::createDoubleCurve(const QmlDesigner::QmlTimelin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimationCurve(keyframes);
|
return AnimationCurve(typeFrom(group), keyframes);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End namespace QmlDesigner.
|
} // End namespace QmlDesigner.
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ class CurveEditorModel : public TreeModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void setStatusLineMsg(const QString& msg);
|
||||||
|
|
||||||
void commitCurrentFrame(int frame);
|
void commitCurrentFrame(int frame);
|
||||||
|
|
||||||
void commitStartFrame(int frame);
|
void commitStartFrame(int frame);
|
||||||
@@ -63,6 +65,8 @@ public:
|
|||||||
|
|
||||||
~CurveEditorModel() override;
|
~CurveEditorModel() override;
|
||||||
|
|
||||||
|
int currentFrame() const;
|
||||||
|
|
||||||
double minimumTime() const;
|
double minimumTime() const;
|
||||||
|
|
||||||
double maximumTime() const;
|
double maximumTime() const;
|
||||||
@@ -92,10 +96,14 @@ private:
|
|||||||
|
|
||||||
AnimationCurve createAnimationCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group);
|
AnimationCurve createAnimationCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group);
|
||||||
|
|
||||||
|
AnimationCurve createBooleanCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group);
|
||||||
|
|
||||||
AnimationCurve createDoubleCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group);
|
AnimationCurve createDoubleCurve(const QmlDesigner::QmlTimelineKeyframeGroup &group);
|
||||||
|
|
||||||
bool m_hasTimeline = false;
|
bool m_hasTimeline = false;
|
||||||
|
|
||||||
|
int m_currentFrame = 0;
|
||||||
|
|
||||||
double m_minTime = 0.;
|
double m_minTime = 0.;
|
||||||
|
|
||||||
double m_maxTime = 0.;
|
double m_maxTime = 0.;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <variantproperty.h>
|
#include <variantproperty.h>
|
||||||
#include <qmlstate.h>
|
#include <qmlstate.h>
|
||||||
#include <qmltimeline.h>
|
#include <qmltimeline.h>
|
||||||
|
#include <nodelistproperty.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -214,6 +215,9 @@ void CurveEditorView::propertiesRemoved(const QList<AbstractProperty> &propertyL
|
|||||||
|
|
||||||
QmlTimeline CurveEditorView::activeTimeline() const
|
QmlTimeline CurveEditorView::activeTimeline() const
|
||||||
{
|
{
|
||||||
|
if (!isAttached())
|
||||||
|
return {};
|
||||||
|
|
||||||
QmlModelState state = currentState();
|
QmlModelState state = currentState();
|
||||||
if (state.isBaseState()) {
|
if (state.isBaseState()) {
|
||||||
for (const ModelNode &node : allModelNodesOfType("QtQuick.Timeline.Timeline")) {
|
for (const ModelNode &node : allModelNodesOfType("QtQuick.Timeline.Timeline")) {
|
||||||
@@ -334,42 +338,56 @@ void commitAuxiliaryData(ModelNode &node, TreeItem *item)
|
|||||||
|
|
||||||
void CurveEditorView::commitKeyframes(TreeItem *item)
|
void CurveEditorView::commitKeyframes(TreeItem *item)
|
||||||
{
|
{
|
||||||
|
if (!isAttached())
|
||||||
|
return;
|
||||||
|
|
||||||
if (auto *nitem = item->asNodeItem()) {
|
if (auto *nitem = item->asNodeItem()) {
|
||||||
ModelNode node = modelNodeForId(nitem->name());
|
ModelNode node = modelNodeForId(nitem->name());
|
||||||
commitAuxiliaryData(node, item);
|
commitAuxiliaryData(node, item);
|
||||||
|
|
||||||
} else if (auto *pitem = item->asPropertyItem()) {
|
} else if (auto *pitem = item->asPropertyItem()) {
|
||||||
QmlTimeline currentTimeline = activeTimeline();
|
QmlTimeline currentTimeline = activeTimeline();
|
||||||
|
if (!currentTimeline.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
QmlTimelineKeyframeGroup group = timelineKeyframeGroup(currentTimeline, pitem);
|
QmlTimelineKeyframeGroup group = timelineKeyframeGroup(currentTimeline, pitem);
|
||||||
|
|
||||||
if (group.isValid()) {
|
if (group.isValid()) {
|
||||||
ModelNode groupNode = group.modelNode();
|
ModelNode groupNode = group.modelNode();
|
||||||
commitAuxiliaryData(groupNode, item);
|
commitAuxiliaryData(groupNode, item);
|
||||||
|
|
||||||
auto replaceKeyframes = [&group, pitem, this]() {
|
auto replaceKeyframes = [&group, pitem, this]() mutable {
|
||||||
m_block = true;
|
m_block = true;
|
||||||
for (auto frame : group.keyframes())
|
|
||||||
|
for (auto& frame : group.keyframes())
|
||||||
frame.destroy();
|
frame.destroy();
|
||||||
|
|
||||||
Keyframe previous;
|
AnimationCurve curve = pitem->curve();
|
||||||
for (auto &&frame : pitem->curve().keyframes()) {
|
if (curve.valueType() == AnimationCurve::ValueType::Bool) {
|
||||||
QPointF pos = frame.position();
|
for (const auto& frame : curve.keyframes()) {
|
||||||
group.setValue(QVariant(pos.y()), pos.x());
|
QPointF pos = frame.position();
|
||||||
|
group.setValue(QVariant(pos.y()), pos.x());
|
||||||
if (previous.isValid()) {
|
|
||||||
if (frame.interpolation() == Keyframe::Interpolation::Bezier ||
|
|
||||||
frame.interpolation() == Keyframe::Interpolation::Step ) {
|
|
||||||
CurveSegment segment(previous, frame);
|
|
||||||
if (segment.isValid())
|
|
||||||
attachEasingCurve(group, pos.x(), segment.easingCurve());
|
|
||||||
} else if (frame.interpolation() == Keyframe::Interpolation::Easing) {
|
|
||||||
QVariant data = frame.data();
|
|
||||||
if (data.type() == static_cast<int>(QMetaType::QEasingCurve))
|
|
||||||
attachEasingCurve(group, pos.x(), data.value<QEasingCurve>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Keyframe previous;
|
||||||
|
for (const auto& frame : curve.keyframes()) {
|
||||||
|
QPointF pos = frame.position();
|
||||||
|
group.setValue(QVariant(pos.y()), pos.x());
|
||||||
|
|
||||||
previous = frame;
|
if (previous.isValid()) {
|
||||||
|
if (frame.interpolation() == Keyframe::Interpolation::Bezier ||
|
||||||
|
frame.interpolation() == Keyframe::Interpolation::Step ) {
|
||||||
|
CurveSegment segment(previous, frame);
|
||||||
|
if (segment.isValid())
|
||||||
|
attachEasingCurve(group, pos.x(), segment.easingCurve());
|
||||||
|
} else if (frame.interpolation() == Keyframe::Interpolation::Easing) {
|
||||||
|
QVariant data = frame.data();
|
||||||
|
if (data.type() == static_cast<int>(QMetaType::QEasingCurve))
|
||||||
|
attachEasingCurve(group, pos.x(), data.value<QEasingCurve>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous = frame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_block = false;
|
m_block = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,22 +37,11 @@
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
CurveItem::CurveItem(QGraphicsItem *parent)
|
|
||||||
: CurveEditorItem(parent)
|
|
||||||
, m_id(0)
|
|
||||||
, m_style()
|
|
||||||
, m_type(PropertyTreeItem::ValueType::Undefined)
|
|
||||||
, m_component(PropertyTreeItem::Component::Generic)
|
|
||||||
, m_transform()
|
|
||||||
, m_keyframes()
|
|
||||||
, m_itemDirty(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent)
|
CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent)
|
||||||
: CurveEditorItem(parent)
|
: CurveEditorItem(parent)
|
||||||
, m_id(id)
|
, m_id(id)
|
||||||
, m_style()
|
, m_style()
|
||||||
, m_type(PropertyTreeItem::ValueType::Undefined)
|
, m_type(curve.valueType())
|
||||||
, m_component(PropertyTreeItem::Component::Generic)
|
, m_component(PropertyTreeItem::Component::Generic)
|
||||||
, m_transform()
|
, m_transform()
|
||||||
, m_keyframes()
|
, m_keyframes()
|
||||||
@@ -235,14 +224,16 @@ PropertyTreeItem::Component CurveItem::component() const
|
|||||||
return m_component;
|
return m_component;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationCurve CurveItem::curve() const
|
AnimationCurve CurveItem::curve(bool remap) const
|
||||||
{
|
{
|
||||||
std::vector<Keyframe> frames;
|
std::vector<Keyframe> frames;
|
||||||
frames.reserve(m_keyframes.size());
|
frames.reserve(m_keyframes.size());
|
||||||
for (auto *frameItem : m_keyframes)
|
|
||||||
frames.push_back(frameItem->keyframe());
|
|
||||||
|
|
||||||
return AnimationCurve(frames);
|
bool map = (m_type == AnimationCurve::ValueType::Bool) && remap;
|
||||||
|
for (auto *frameItem : m_keyframes)
|
||||||
|
frames.push_back(frameItem->keyframe(map));
|
||||||
|
|
||||||
|
return AnimationCurve(m_type, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationCurve CurveItem::resolvedCurve() const
|
AnimationCurve CurveItem::resolvedCurve() const
|
||||||
@@ -278,11 +269,13 @@ std::vector<AnimationCurve> CurveItem::curves() const
|
|||||||
Keyframe previous = tmp.back();
|
Keyframe previous = tmp.back();
|
||||||
|
|
||||||
if (tmp.size() >= 2)
|
if (tmp.size() >= 2)
|
||||||
out.push_back(AnimationCurve(tmp));
|
out.push_back(AnimationCurve(m_type, tmp));
|
||||||
|
|
||||||
out.push_back(AnimationCurve(current.data().value<QEasingCurve>(),
|
out.push_back(AnimationCurve(
|
||||||
previous.position(),
|
m_type,
|
||||||
current.position()));
|
current.data().value<QEasingCurve>(),
|
||||||
|
previous.position(),
|
||||||
|
current.position()));
|
||||||
|
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
tmp.push_back(current);
|
tmp.push_back(current);
|
||||||
@@ -293,7 +286,7 @@ std::vector<AnimationCurve> CurveItem::curves() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!tmp.empty())
|
if (!tmp.empty())
|
||||||
out.push_back(AnimationCurve(tmp));
|
out.push_back(AnimationCurve(m_type, tmp));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -384,7 +377,7 @@ void CurveItem::setDirty(bool dirty)
|
|||||||
|
|
||||||
void CurveItem::setHandleVisibility(bool visible)
|
void CurveItem::setHandleVisibility(bool visible)
|
||||||
{
|
{
|
||||||
for (auto frame : qAsConst(m_keyframes))
|
for (auto *frame : qAsConst(m_keyframes))
|
||||||
frame->setHandleVisibility(visible);
|
frame->setHandleVisibility(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +395,7 @@ void CurveItem::setCurve(const AnimationCurve &curve)
|
|||||||
{
|
{
|
||||||
freeClear(m_keyframes);
|
freeClear(m_keyframes);
|
||||||
|
|
||||||
for (const auto &frame : curve.keyframes()) {
|
for (const auto& frame : curve.keyframes()) {
|
||||||
auto *item = new KeyframeItem(frame, this);
|
auto *item = new KeyframeItem(frame, this);
|
||||||
item->setLocked(locked());
|
item->setLocked(locked());
|
||||||
item->setComponentTransform(m_transform);
|
item->setComponentTransform(m_transform);
|
||||||
@@ -419,7 +412,7 @@ QRectF CurveItem::setComponentTransform(const QTransform &transform)
|
|||||||
{
|
{
|
||||||
prepareGeometryChange();
|
prepareGeometryChange();
|
||||||
m_transform = transform;
|
m_transform = transform;
|
||||||
for (auto frame : qAsConst(m_keyframes))
|
for (auto *frame : qAsConst(m_keyframes))
|
||||||
frame->setComponentTransform(transform);
|
frame->setComponentTransform(transform);
|
||||||
|
|
||||||
return boundingRect();
|
return boundingRect();
|
||||||
@@ -438,6 +431,14 @@ void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
|
|||||||
if (m_keyframes.empty())
|
if (m_keyframes.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (m_type == AnimationCurve::ValueType::Bool) {
|
||||||
|
if (interpolation != Keyframe::Interpolation::Step) {
|
||||||
|
interpolation = Keyframe::Interpolation::Step;
|
||||||
|
QString msg("Warning: Curves of type bool can only be step-interpolated!");
|
||||||
|
emit curveMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyframeItem *prevItem = m_keyframes[0];
|
KeyframeItem *prevItem = m_keyframes[0];
|
||||||
for (int i = 1; i < m_keyframes.size(); ++i) {
|
for (int i = 1; i < m_keyframes.size(); ++i) {
|
||||||
KeyframeItem *currItem = m_keyframes[i];
|
KeyframeItem *currItem = m_keyframes[i];
|
||||||
@@ -454,7 +455,7 @@ void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
|
|||||||
prevItem = currItem;
|
prevItem = currItem;
|
||||||
}
|
}
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
emit curveChanged(id(), curve());
|
emit curveChanged(id(), curve(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveItem::setDefaultInterpolation()
|
void CurveItem::setDefaultInterpolation()
|
||||||
@@ -466,7 +467,7 @@ void CurveItem::setDefaultInterpolation()
|
|||||||
if (frame->selected())
|
if (frame->selected())
|
||||||
frame->setDefaultInterpolation();
|
frame->setDefaultInterpolation();
|
||||||
}
|
}
|
||||||
emit curveChanged(id(), curve());
|
emit curveChanged(id(), curve(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveItem::toggleUnified()
|
void CurveItem::toggleUnified()
|
||||||
@@ -478,12 +479,13 @@ void CurveItem::toggleUnified()
|
|||||||
if (frame->selected())
|
if (frame->selected())
|
||||||
frame->toggleUnified();
|
frame->toggleUnified();
|
||||||
}
|
}
|
||||||
emit curveChanged(id(), curve());
|
emit curveChanged(id(), curve(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveItem::connect(GraphicsScene *scene)
|
void CurveItem::connect(GraphicsScene *scene)
|
||||||
{
|
{
|
||||||
QObject::connect(this, &CurveItem::curveChanged, scene, &GraphicsScene::curveChanged);
|
QObject::connect(this, &CurveItem::curveChanged, scene, &GraphicsScene::curveChanged);
|
||||||
|
QObject::connect(this, &CurveItem::curveMessage, scene, &GraphicsScene::curveMessage);
|
||||||
|
|
||||||
QObject::connect(this, &CurveItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
|
QObject::connect(this, &CurveItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
|
||||||
QObject::connect(this, &CurveItem::handleMoved, scene, &GraphicsScene::handleMoved);
|
QObject::connect(this, &CurveItem::handleMoved, scene, &GraphicsScene::handleMoved);
|
||||||
@@ -498,7 +500,7 @@ void CurveItem::insertKeyframeByTime(double time)
|
|||||||
acurve.insert(time);
|
acurve.insert(time);
|
||||||
setCurve(acurve);
|
setCurve(acurve);
|
||||||
|
|
||||||
emit curveChanged(id(), curve());
|
emit curveChanged(id(), curve(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveItem::deleteSelectedKeyframes()
|
void CurveItem::deleteSelectedKeyframes()
|
||||||
@@ -516,7 +518,14 @@ void CurveItem::deleteSelectedKeyframes()
|
|||||||
|
|
||||||
markDirty();
|
markDirty();
|
||||||
|
|
||||||
emit curveChanged(id(), curve());
|
emit curveChanged(id(), curve(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurveItem::remapValue(double min, double max)
|
||||||
|
{
|
||||||
|
for (auto *frameItem : qAsConst(m_keyframes)) {
|
||||||
|
frameItem->remapValue(min, max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveItem::markDirty()
|
void CurveItem::markDirty()
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class CurveItem : public CurveEditorItem
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void curveMessage(const QString& msg);
|
||||||
|
|
||||||
void curveChanged(unsigned int id, const AnimationCurve &curve);
|
void curveChanged(unsigned int id, const AnimationCurve &curve);
|
||||||
|
|
||||||
void keyframeMoved(KeyframeItem *item, const QPointF &direction);
|
void keyframeMoved(KeyframeItem *item, const QPointF &direction);
|
||||||
@@ -53,8 +55,6 @@ signals:
|
|||||||
void handleMoved(KeyframeItem *frame, HandleItem::Slot slot, double angle, double deltaLength);
|
void handleMoved(KeyframeItem *frame, HandleItem::Slot slot, double angle, double deltaLength);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CurveItem(QGraphicsItem *parent = nullptr);
|
|
||||||
|
|
||||||
CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent = nullptr);
|
CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent = nullptr);
|
||||||
|
|
||||||
~CurveItem() override;
|
~CurveItem() override;
|
||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
|
|
||||||
PropertyTreeItem::Component component() const;
|
PropertyTreeItem::Component component() const;
|
||||||
|
|
||||||
AnimationCurve curve() const;
|
AnimationCurve curve(bool remap = false) const;
|
||||||
|
|
||||||
AnimationCurve resolvedCurve() const;
|
AnimationCurve resolvedCurve() const;
|
||||||
|
|
||||||
@@ -135,6 +135,8 @@ public:
|
|||||||
|
|
||||||
void deleteSelectedKeyframes();
|
void deleteSelectedKeyframes();
|
||||||
|
|
||||||
|
void remapValue(double min, double max);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void markDirty();
|
void markDirty();
|
||||||
|
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
|
|||||||
if (curve->isDirty()) {
|
if (curve->isDirty()) {
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
curve->setDirty(false);
|
curve->setDirty(false);
|
||||||
emit curveChanged(curve->id(), curve->curve());
|
emit curveChanged(curve->id(), curve->curve(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ class GraphicsScene : public QGraphicsScene
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void curveMessage(const QString& msg);
|
||||||
|
|
||||||
void curveChanged(unsigned int id, const AnimationCurve &curve);
|
void curveChanged(unsigned int id, const AnimationCurve &curve);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
|
|||||||
|
|
||||||
connect(&m_dialog, &CurveEditorStyleDialog::styleChanged, this, &GraphicsView::setStyle);
|
connect(&m_dialog, &CurveEditorStyleDialog::styleChanged, this, &GraphicsView::setStyle);
|
||||||
|
|
||||||
|
connect(m_scene, &GraphicsScene::curveMessage, m_model, &CurveEditorModel::setStatusLineMsg);
|
||||||
|
|
||||||
auto itemSlot = [this](unsigned int id, const AnimationCurve &curve) {
|
auto itemSlot = [this](unsigned int id, const AnimationCurve &curve) {
|
||||||
m_model->setCurve(id, curve);
|
m_model->setCurve(id, curve);
|
||||||
applyZoom(m_zoomX, m_zoomY);
|
applyZoom(m_zoomX, m_zoomY);
|
||||||
@@ -560,7 +562,14 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
|
|||||||
scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
|
scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto *curve : m_scene->curves()) {
|
||||||
|
if (curve->valueType() == AnimationCurve::ValueType::Bool) {
|
||||||
|
curve->remapValue(minValue, maxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_scene->doNotMoveItems(false);
|
m_scene->doNotMoveItems(false);
|
||||||
|
this->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsView::drawGrid(QPainter *painter)
|
void GraphicsView::drawGrid(QPainter *painter)
|
||||||
|
|||||||
@@ -103,8 +103,16 @@ void KeyframeItem::lockedCallback()
|
|||||||
|
|
||||||
KeyframeItem::~KeyframeItem() {}
|
KeyframeItem::~KeyframeItem() {}
|
||||||
|
|
||||||
Keyframe KeyframeItem::keyframe() const
|
Keyframe KeyframeItem::keyframe(bool remap) const
|
||||||
{
|
{
|
||||||
|
if (remap) {
|
||||||
|
auto frame = m_frame;
|
||||||
|
auto pos = frame.position();
|
||||||
|
auto center = m_min + ((m_max - m_min) / 2.0);
|
||||||
|
pos.ry() = pos.y() > center ? 1.0 : 0.0;
|
||||||
|
frame.setPosition(pos);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
return m_frame;
|
return m_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +358,18 @@ void KeyframeItem::moveHandle(HandleItem::Slot slot, double deltaAngle, double d
|
|||||||
emit redrawCurve();
|
emit redrawCurve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyframeItem::remapValue(double min, double max)
|
||||||
|
{
|
||||||
|
auto center = m_min + ((m_max - m_min) / 2.0);
|
||||||
|
auto pos = m_frame.position();
|
||||||
|
pos.ry() = pos.y() > center ? max : min;
|
||||||
|
m_frame.setPosition(pos);
|
||||||
|
|
||||||
|
m_max = max;
|
||||||
|
m_min = min;
|
||||||
|
setKeyframe(m_frame);
|
||||||
|
}
|
||||||
|
|
||||||
void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged)
|
void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -422,8 +442,10 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
|
|||||||
|
|
||||||
if (curveItem->valueType() == PropertyTreeItem::ValueType::Integer)
|
if (curveItem->valueType() == PropertyTreeItem::ValueType::Integer)
|
||||||
position.setY(std::round(position.y()));
|
position.setY(std::round(position.y()));
|
||||||
else if (curveItem->valueType() == PropertyTreeItem::ValueType::Bool)
|
else if (curveItem->valueType() == PropertyTreeItem::ValueType::Bool) {
|
||||||
position.setY(position.y() > 0.5 ? 1.0 : 0.0);
|
double center = m_min + ((m_max - m_min) / 2.0);
|
||||||
|
position.setY(position.y() > center ? m_max : m_min);
|
||||||
|
}
|
||||||
|
|
||||||
if (!legalLeft() || !legalRight()) {
|
if (!legalLeft() || !legalRight()) {
|
||||||
return QVariant(m_transform.map(position));
|
return QVariant(m_transform.map(position));
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
|
|
||||||
void lockedCallback() override;
|
void lockedCallback() override;
|
||||||
|
|
||||||
Keyframe keyframe() const;
|
Keyframe keyframe(bool remap = false) const;
|
||||||
|
|
||||||
bool isUnified() const;
|
bool isUnified() const;
|
||||||
|
|
||||||
@@ -106,6 +106,8 @@ public:
|
|||||||
|
|
||||||
void moveHandle(HandleItem::Slot slot, double deltaAngle, double deltaLength);
|
void moveHandle(HandleItem::Slot slot, double deltaAngle, double deltaLength);
|
||||||
|
|
||||||
|
void remapValue(double min, double max);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||||
|
|
||||||
@@ -134,6 +136,9 @@ private:
|
|||||||
QPointF m_validPos;
|
QPointF m_validPos;
|
||||||
|
|
||||||
bool m_visibleOverride = true;
|
bool m_visibleOverride = true;
|
||||||
|
|
||||||
|
double m_min = 0.0;
|
||||||
|
double m_max = 1.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace QmlDesigner.
|
} // End namespace QmlDesigner.
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ CurveItem *TreeModel::curveItem(TreeItem *item)
|
|||||||
{
|
{
|
||||||
if (auto *pti = item->asPropertyItem()) {
|
if (auto *pti = item->asPropertyItem()) {
|
||||||
auto *citem = new CurveItem(pti->id(), pti->curve());
|
auto *citem = new CurveItem(pti->id(), pti->curve());
|
||||||
citem->setValueType(pti->valueType());
|
|
||||||
citem->setComponent(pti->component());
|
citem->setComponent(pti->component());
|
||||||
citem->setLocked(pti->locked() || item->implicitlyLocked());
|
citem->setLocked(pti->locked() || item->implicitlyLocked());
|
||||||
citem->setPinned(pti->pinned() || item->implicitlyPinned());
|
citem->setPinned(pti->pinned() || item->implicitlyPinned());
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace QmlDesigner {
|
|||||||
class Keyframe
|
class Keyframe
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class ValueType { Undefined, Bool, Integer, Double };
|
||||||
enum class Interpolation { Undefined, Step, Linear, Bezier, Easing };
|
enum class Interpolation { Undefined, Step, Linear, Bezier, Easing };
|
||||||
|
|
||||||
Keyframe();
|
Keyframe();
|
||||||
|
|||||||
@@ -321,11 +321,9 @@ std::vector<PropertyTreeItem *> NodeTreeItem::properties() const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_type(curve.valueType())
|
||||||
, m_component(Component::Generic)
|
, m_component(Component::Generic)
|
||||||
, m_curve(curve)
|
, m_curve(curve)
|
||||||
{}
|
{}
|
||||||
|
|||||||
@@ -151,15 +151,10 @@ class PropertyTreeItem : public TreeItem
|
|||||||
public:
|
public:
|
||||||
enum class Component { Generic, R, G, B, A, X, Y, Z, W };
|
enum class Component { Generic, R, G, B, A, X, Y, Z, W };
|
||||||
|
|
||||||
enum class ValueType {
|
using ValueType = AnimationCurve::ValueType;
|
||||||
Undefined,
|
|
||||||
Bool,
|
|
||||||
Integer,
|
|
||||||
Double,
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PropertyTreeItem(const QString &name, const AnimationCurve &curve, const ValueType &type);
|
PropertyTreeItem(const QString &name, const AnimationCurve &curve);
|
||||||
|
|
||||||
PropertyTreeItem *asPropertyItem() override;
|
PropertyTreeItem *asPropertyItem() override;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user