aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner/components/curveeditor
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmldesigner/components/curveeditor')
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp187
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/animationcurve.h69
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp64
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.h53
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.pri46
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp70
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h72
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditorstyle.h125
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp101
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.h63
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.cpp267
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.h127
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp226
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h98
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.cpp277
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.h65
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp225
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h89
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp523
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h157
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp104
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/handleitem.h56
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp251
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h98
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp187
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/playhead.h72
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.cpp112
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.h85
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/selector.cpp221
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/selector.h86
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/shortcut.cpp81
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/shortcut.h61
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.cpp146
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.h63
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treemodel.cpp156
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treemodel.h75
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treeview.cpp127
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/treeview.h61
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp107
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/utils.h52
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/keyframe.cpp88
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/keyframe.h65
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/treeitem.cpp227
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/treeitem.h137
44 files changed, 5622 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp
new file mode 100644
index 0000000000..8e2d5224e1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/animationcurve.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** 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 "animationcurve.h"
+#include "detail/curvesegment.h"
+
+#include <QLineF>
+
+namespace DesignTools {
+
+AnimationCurve::AnimationCurve()
+ : m_frames()
+{}
+
+AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
+ : m_frames(frames)
+ , m_minY(std::numeric_limits<double>::max())
+ , m_maxY(std::numeric_limits<double>::lowest())
+{
+ if (isValid()) {
+
+ 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();
+ }
+ }
+}
+
+bool AnimationCurve::isValid() const
+{
+ return m_frames.size() >= 2;
+}
+
+double AnimationCurve::minimumTime() const
+{
+ if (!m_frames.empty())
+ return m_frames.front().position().x();
+
+ return std::numeric_limits<double>::max();
+}
+
+double AnimationCurve::maximumTime() const
+{
+ if (!m_frames.empty())
+ return m_frames.back().position().x();
+
+ return std::numeric_limits<double>::lowest();
+}
+
+double AnimationCurve::minimumValue() const
+{
+ return m_minY;
+}
+
+double AnimationCurve::maximumValue() const
+{
+ return m_maxY;
+}
+
+std::vector<Keyframe> AnimationCurve::keyframes() const
+{
+ return m_frames;
+}
+
+std::vector<QPointF> AnimationCurve::extrema() const
+{
+ std::vector<QPointF> out;
+
+ CurveSegment segment;
+ 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();
+ out.insert(std::end(out), std::begin(es), std::end(es));
+
+ segment.setLeft(m_frames[i]);
+ }
+
+ return out;
+}
+
+std::vector<double> AnimationCurve::yForX(double x) const
+{
+ if (m_frames.front().position().x() > x)
+ return std::vector<double>();
+
+ CurveSegment segment;
+ for (auto &frame : m_frames) {
+ if (frame.position().x() > x) {
+ segment.setRight(frame);
+ return segment.yForX(x);
+ }
+ segment.setLeft(frame);
+ }
+ return std::vector<double>();
+}
+
+std::vector<double> AnimationCurve::xForY(double y, uint segment) const
+{
+ if (m_frames.size() > segment + 1) {
+ CurveSegment seg(m_frames[segment], m_frames[segment + 1]);
+ return seg.xForY(y);
+ }
+ return std::vector<double>();
+}
+
+bool AnimationCurve::intersects(const QPointF &coord, double radius)
+{
+ if (m_frames.size() < 2)
+ return false;
+
+ std::vector<CurveSegment> influencer;
+
+ CurveSegment current;
+ current.setLeft(m_frames.at(0));
+
+ for (size_t i = 1; i < m_frames.size(); ++i) {
+ Keyframe &frame = m_frames.at(i);
+
+ current.setRight(frame);
+
+ if (current.containsX(coord.x() - radius) ||
+ current.containsX(coord.x()) ||
+ current.containsX(coord.x() + radius)) {
+ influencer.push_back(current);
+ }
+
+ if (frame.position().x() > coord.x() + radius)
+ break;
+
+ current.setLeft(frame);
+ }
+
+ for (auto &segment : influencer) {
+ for (auto &y : segment.yForX(coord.x())) {
+ QLineF line(coord.x(), y, coord.x(), coord.y());
+ 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;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/animationcurve.h b/src/plugins/qmldesigner/components/curveeditor/animationcurve.h
new file mode 100644
index 0000000000..0533e479a1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/animationcurve.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 "keyframe.h"
+
+#include <vector>
+
+namespace DesignTools {
+
+class AnimationCurve
+{
+public:
+ AnimationCurve();
+
+ AnimationCurve(const std::vector<Keyframe> &frames);
+
+ bool isValid() const;
+
+ double minimumTime() const;
+
+ double maximumTime() const;
+
+ double minimumValue() const;
+
+ double maximumValue() const;
+
+ std::vector<Keyframe> keyframes() const;
+
+ std::vector<QPointF> extrema() const;
+
+ std::vector<double> yForX(double x) const;
+
+ std::vector<double> xForY(double y, uint segment) const;
+
+ bool intersects(const QPointF &coord, double radius);
+
+private:
+ std::vector<Keyframe> m_frames;
+
+ double m_minY;
+
+ double m_maxY;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
new file mode 100644
index 0000000000..4eba31c6bd
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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 "curveeditor.h"
+#include "curveeditormodel.h"
+#include "detail/curveitem.h"
+#include "detail/graphicsview.h"
+#include "detail/treeview.h"
+
+#include <QHBoxLayout>
+#include <QSplitter>
+
+namespace DesignTools {
+
+CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
+ : QWidget(parent)
+ , m_tree(new TreeView(model, this))
+ , m_view(new GraphicsView(model))
+{
+ QSplitter *splitter = new QSplitter;
+ splitter->addWidget(m_tree);
+ splitter->addWidget(m_view);
+ splitter->setStretchFactor(1, 2);
+
+ QHBoxLayout *box = new QHBoxLayout;
+ box->addWidget(splitter);
+ setLayout(box);
+
+ connect(m_tree, &TreeView::curvesSelected, m_view, &GraphicsView::reset);
+}
+
+void CurveEditor::zoomX(double zoom)
+{
+ m_view->setZoomX(zoom);
+}
+
+void CurveEditor::zoomY(double zoom)
+{
+ m_view->setZoomY(zoom);
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.h b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h
new file mode 100644
index 0000000000..a2c5873be0
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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 <QWidget>
+
+namespace DesignTools {
+
+class CurveEditorModel;
+class GraphicsView;
+class TreeView;
+
+class CurveEditor : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CurveEditor(CurveEditorModel *model, QWidget *parent = nullptr);
+
+ void zoomX(double zoom);
+
+ void zoomY(double zoom);
+
+private:
+ TreeView *m_tree;
+
+ GraphicsView *m_view;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri
new file mode 100644
index 0000000000..31ffe5d818
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.pri
@@ -0,0 +1,46 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/animationcurve.h \
+ $$PWD/curveeditor.h \
+ $$PWD/curveeditormodel.h \
+ $$PWD/detail/colorcontrol.h \
+ $$PWD/detail/curveeditorstyledialog.h \
+ $$PWD/detail/curveitem.h \
+ $$PWD/detail/curvesegment.h \
+ $$PWD/detail/graphicsscene.h \
+ $$PWD/detail/graphicsview.h \
+ $$PWD/detail/handleitem.h \
+ $$PWD/detail/keyframeitem.h \
+ $$PWD/detail/playhead.h \
+ $$PWD/detail/selectableitem.h \
+ $$PWD/detail/selector.h \
+ $$PWD/detail/shortcut.h \
+ $$PWD/detail/treeitemdelegate.h \
+ $$PWD/detail/treemodel.h \
+ $$PWD/detail/treeview.h \
+ $$PWD/keyframe.h \
+ $$PWD/treeitem.h
+
+SOURCES += \
+ $$PWD/animationcurve.cpp \
+ $$PWD/curveeditor.cpp \
+ $$PWD/curveeditormodel.cpp \
+ $$PWD/detail/colorcontrol.cpp \
+ $$PWD/detail/curveeditorstyledialog.cpp \
+ $$PWD/detail/curveitem.cpp \
+ $$PWD/detail/curvesegment.cpp \
+ $$PWD/detail/graphicsscene.cpp \
+ $$PWD/detail/graphicsview.cpp \
+ $$PWD/detail/handleitem.cpp \
+ $$PWD/detail/keyframeitem.cpp \
+ $$PWD/detail/playhead.cpp \
+ $$PWD/detail/selectableitem.cpp \
+ $$PWD/detail/selector.cpp \
+ $$PWD/detail/shortcut.cpp \
+ $$PWD/detail/treeitemdelegate.cpp \
+ $$PWD/detail/treemodel.cpp \
+ $$PWD/detail/treeview.cpp \
+ $$PWD/detail/utils.cpp \
+ $$PWD/keyframe.cpp \
+ $$PWD/treeitem.cpp
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp
new file mode 100644
index 0000000000..3b8b26b763
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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 "curveeditormodel.h"
+#include "treeitem.h"
+#include "detail/graphicsview.h"
+
+namespace DesignTools {
+
+CurveEditorModel::CurveEditorModel(QObject *parent)
+ : TreeModel(parent)
+{}
+
+CurveEditorModel::~CurveEditorModel() {}
+
+
+void CurveEditorModel::setCurrentFrame(int frame)
+{
+ if (graphicsView())
+ graphicsView()->setCurrentFrame(frame);
+}
+
+void CurveEditorModel::setCurve(unsigned int id, const AnimationCurve &curve)
+{
+ if (TreeItem *item = find(id)) {
+ if (PropertyTreeItem *propertyItem = item->asPropertyItem()) {
+ propertyItem->setCurve(curve);
+ emit curveChanged(propertyItem);
+ }
+ }
+}
+
+void CurveEditorModel::reset(const std::vector<TreeItem *> &items)
+{
+ beginResetModel();
+
+ initialize();
+
+ unsigned int counter = 0;
+ for (auto *item : items) {
+ item->setId(++counter);
+ root()->addChild(item);
+ }
+
+ endResetModel();
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h
new file mode 100644
index 0000000000..6e212d4c6a
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditormodel.h
@@ -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 "detail/treemodel.h"
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+class QPointF;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+struct CurveEditorStyle;
+
+class AnimationCurve;
+class PropertyTreeItem;
+class TreeItem;
+
+class CurveEditorModel : public TreeModel
+{
+ Q_OBJECT
+
+signals:
+ void currentFrameChanged(int frame);
+
+ void curveChanged(PropertyTreeItem *item);
+
+public:
+ virtual double minimumTime() const = 0;
+
+ virtual double maximumTime() const = 0;
+
+ virtual CurveEditorStyle style() const = 0;
+
+public:
+ CurveEditorModel(QObject *parent = nullptr);
+
+ ~CurveEditorModel() override;
+
+ void setCurrentFrame(int frame);
+
+ void setCurve(unsigned int id, const AnimationCurve &curve);
+
+ void reset(const std::vector<TreeItem *> &items);
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorstyle.h b/src/plugins/qmldesigner/components/curveeditor/curveeditorstyle.h
new file mode 100644
index 0000000000..03ea11c8c1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorstyle.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** 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 "detail/shortcut.h"
+
+#include <QBitmap>
+#include <QBrush>
+#include <QColor>
+#include <QDialog>
+#include <QIcon>
+#include <QKeySequence>
+
+namespace DesignTools {
+
+struct TreeItemStyleOption
+{
+ double margins;
+ QIcon pinnedIcon = QIcon(":/ICON_PINNED");
+ QIcon unpinnedIcon = QIcon(":/ICON_UNPINNED");
+ QIcon lockedIcon = QIcon(":/ICON_LOCKED");
+ QIcon unlockedIcon = QIcon(":/ICON_UNLOCKED");
+};
+
+struct HandleItemStyleOption
+{
+ double size = 10.0;
+ double lineWidth = 1.0;
+ QColor color = QColor(200, 0, 0);
+ QColor selectionColor = QColor(200, 200, 200);
+};
+
+struct KeyframeItemStyleOption
+{
+ double size = 10.0;
+ QColor color = QColor(200, 200, 0);
+ QColor selectionColor = QColor(200, 200, 200);
+};
+
+struct CurveItemStyleOption
+{
+ double width = 1.0;
+ QColor color = QColor(0, 200, 0);
+ QColor selectionColor = QColor(200, 200, 200);
+};
+
+struct PlayheadStyleOption
+{
+ double width = 20.0;
+ double radius = 4.0;
+ QColor color = QColor(200, 200, 0);
+};
+
+struct Shortcuts
+{
+ Shortcut newSelection = Shortcut(Qt::LeftButton);
+ Shortcut addToSelection = Shortcut(Qt::LeftButton, Qt::ControlModifier | Qt::ShiftModifier);
+ Shortcut removeFromSelection = Shortcut(Qt::LeftButton, Qt::ShiftModifier);
+ Shortcut toggleSelection = Shortcut(Qt::LeftButton, Qt::ControlModifier);
+
+ Shortcut zoom = Shortcut(Qt::RightButton, Qt::AltModifier);
+ Shortcut pan = Shortcut(Qt::MiddleButton, Qt::AltModifier);
+ Shortcut frameAll = Shortcut(Qt::NoModifier, Qt::Key_A);
+};
+
+struct CurveEditorStyle
+{
+ Shortcuts shortcuts;
+
+ QBrush backgroundBrush = QBrush(QColor(5, 0, 100));
+ QBrush backgroundAlternateBrush = QBrush(QColor(0, 0, 50));
+ QColor fontColor = QColor(200, 200, 200);
+ QColor gridColor = QColor(128, 128, 128);
+ double canvasMargin = 5.0;
+ int zoomInWidth = 100;
+ int zoomInHeight = 100;
+ double timeAxisHeight = 40.0;
+ double timeOffsetLeft = 10.0;
+ double timeOffsetRight = 10.0;
+ QColor rangeBarColor = QColor(128, 128, 128);
+ QColor rangeBarCapsColor = QColor(50, 50, 255);
+ double valueAxisWidth = 60.0;
+ double valueOffsetTop = 10.0;
+ double valueOffsetBottom = 10.0;
+
+ HandleItemStyleOption handleStyle;
+ KeyframeItemStyleOption keyframeStyle;
+ CurveItemStyleOption curveStyle;
+ TreeItemStyleOption treeItemStyle;
+ PlayheadStyleOption playhead;
+};
+
+inline QPixmap pixmapFromIcon(const QIcon &icon, const QSize &size, const QColor &color)
+{
+ QPixmap pixmap = icon.pixmap(size);
+ QPixmap mask(pixmap.size());
+ mask.fill(color);
+ mask.setMask(pixmap.createMaskFromColor(Qt::transparent));
+ return mask;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp
new file mode 100644
index 0000000000..a833a4c6ff
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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 "colorcontrol.h"
+
+#include <QColorDialog>
+#include <QEvent>
+#include <QHelpEvent>
+#include <QPainter>
+#include <QToolTip>
+
+namespace DesignTools {
+
+ColorControl::ColorControl()
+ : QWidget(nullptr)
+ , m_color(Qt::black)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ setFixedHeight(20);
+}
+
+ColorControl::ColorControl(const QColor &color, QWidget *parent)
+ : QWidget(parent)
+ , m_color(color)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ setFixedHeight(20);
+}
+
+ColorControl::~ColorControl() = default;
+
+QColor ColorControl::value() const
+{
+ return m_color;
+}
+
+void ColorControl::setValue(const QColor &val)
+{
+ m_color = val;
+}
+
+bool ColorControl::event(QEvent *event)
+{
+ if (event->type() == QEvent::ToolTip) {
+ if (auto helpEvent = static_cast<const QHelpEvent *>(event)) {
+ QToolTip::showText(helpEvent->globalPos(), m_color.name());
+ return true;
+ }
+ }
+ return QWidget::event(event);
+}
+
+void ColorControl::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ painter.fillRect(event->rect(), m_color);
+}
+
+void ColorControl::mouseReleaseEvent(QMouseEvent *event)
+{
+ QColor color = QColorDialog::getColor(m_color, this);
+
+ event->accept();
+
+ if (color != m_color) {
+ m_color = color;
+ update();
+ emit valueChanged();
+ }
+}
+
+void ColorControl::mousePressEvent(QMouseEvent *event)
+{
+ // Required if embedded in a QGraphicsProxywidget
+ // in order to call mouseRelease properly.
+ QWidget::mousePressEvent(event);
+ event->accept();
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.h b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.h
new file mode 100644
index 0000000000..54dfe194f8
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/colorcontrol.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 <QWidget>
+
+namespace DesignTools {
+
+class ColorControl : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ColorControl();
+
+ ColorControl(const QColor &color, QWidget *parent = nullptr);
+
+ ~ColorControl() override;
+
+ QColor value() const;
+
+ void setValue(const QColor &val);
+
+protected:
+ bool event(QEvent *event) override;
+
+ void paintEvent(QPaintEvent *event) override;
+
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+
+signals:
+ void valueChanged();
+
+private:
+ QColor m_color;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.cpp
new file mode 100644
index 0000000000..a8b653a74d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyledialog.h"
+#include "colorcontrol.h"
+#include "curveeditorstyle.h"
+
+#include <QDebug>
+#include <QDoubleSpinBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+namespace DesignTools {
+
+QHBoxLayout *createRow(const QString &title, QWidget *widget)
+{
+ auto *label = new QLabel(title);
+ label->setFixedWidth(200);
+ label->setAlignment(Qt::AlignRight);
+
+ auto *box = new QHBoxLayout;
+ box->addWidget(label);
+ box->addWidget(widget);
+ return box;
+}
+
+CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent)
+ : QDialog(parent)
+ , m_printButton(new QPushButton("Print"))
+ , m_background(new ColorControl(style.backgroundBrush.color()))
+ , m_backgroundAlternate(new ColorControl(style.backgroundAlternateBrush.color()))
+ , m_fontColor(new ColorControl(style.fontColor))
+ , m_gridColor(new ColorControl(style.gridColor))
+ , m_canvasMargin(new QDoubleSpinBox())
+ , m_zoomInWidth(new QSpinBox())
+ , m_zoomInHeight(new QSpinBox())
+ , m_timeAxisHeight(new QDoubleSpinBox())
+ , m_timeOffsetLeft(new QDoubleSpinBox())
+ , m_timeOffsetRight(new QDoubleSpinBox())
+ , m_rangeBarColor(new ColorControl(style.rangeBarCapsColor))
+ , m_rangeBarCapsColor(new ColorControl(style.rangeBarCapsColor))
+ , m_valueAxisWidth(new QDoubleSpinBox())
+ , m_valueOffsetTop(new QDoubleSpinBox())
+ , m_valueOffsetBottom(new QDoubleSpinBox())
+ , m_handleSize(new QDoubleSpinBox())
+ , m_handleLineWidth(new QDoubleSpinBox())
+ , m_handleColor(new ColorControl(style.handleStyle.color))
+ , m_handleSelectionColor(new ColorControl(style.handleStyle.selectionColor))
+ , m_keyframeSize(new QDoubleSpinBox())
+ , m_keyframeColor(new ColorControl(style.keyframeStyle.color))
+ , m_keyframeSelectionColor(new ColorControl(style.keyframeStyle.selectionColor))
+ , m_curveWidth(new QDoubleSpinBox())
+ , m_curveColor(new ColorControl(style.curveStyle.color))
+ , m_curveSelectionColor(new ColorControl(style.curveStyle.selectionColor))
+ , m_treeMargins(new QDoubleSpinBox())
+ , m_playheadWidth(new QDoubleSpinBox())
+ , m_playheadRadius(new QDoubleSpinBox())
+ , m_playheadColor(new ColorControl(style.playhead.color))
+
+{
+ m_canvasMargin->setValue(style.canvasMargin);
+ m_zoomInWidth->setValue(style.zoomInWidth);
+ m_zoomInHeight->setValue(style.zoomInHeight);
+ m_zoomInHeight->setMaximum(9000);
+
+ m_timeAxisHeight->setValue(style.timeAxisHeight);
+ m_timeOffsetLeft->setValue(style.timeOffsetLeft);
+ m_timeOffsetRight->setValue(style.timeOffsetRight);
+ m_valueAxisWidth->setValue(style.valueAxisWidth);
+ m_valueOffsetTop->setValue(style.valueOffsetTop);
+ m_valueOffsetBottom->setValue(style.valueOffsetBottom);
+ m_handleSize->setValue(style.handleStyle.size);
+ m_handleLineWidth->setValue(style.handleStyle.lineWidth);
+ m_keyframeSize->setValue(style.keyframeStyle.size);
+ m_curveWidth->setValue(style.curveStyle.width);
+ m_treeMargins->setValue(style.treeItemStyle.margins);
+ m_playheadWidth->setValue(style.playhead.width);
+ m_playheadRadius->setValue(style.playhead.radius);
+
+ connect(m_printButton, &QPushButton::released, this, &CurveEditorStyleDialog::printStyle);
+
+ auto intChanged = [this](int) { emitStyleChanged(); };
+ auto doubleChanged = [this](double) { emitStyleChanged(); };
+ auto colorChanged = [this]() { emitStyleChanged(); };
+
+ auto intSignal = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged);
+ auto doubleSignal = static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged);
+
+ connect(m_background, &ColorControl::valueChanged, colorChanged);
+ connect(m_backgroundAlternate, &ColorControl::valueChanged, colorChanged);
+ connect(m_fontColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_gridColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_canvasMargin, doubleSignal, doubleChanged);
+ connect(m_zoomInWidth, intSignal, intChanged);
+ connect(m_zoomInHeight, intSignal, intChanged);
+ connect(m_timeAxisHeight, doubleSignal, doubleChanged);
+ connect(m_timeOffsetLeft, doubleSignal, doubleChanged);
+ connect(m_timeOffsetRight, doubleSignal, doubleChanged);
+ connect(m_rangeBarColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_rangeBarCapsColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_valueAxisWidth, doubleSignal, doubleChanged);
+ connect(m_valueOffsetTop, doubleSignal, doubleChanged);
+ connect(m_valueOffsetBottom, doubleSignal, doubleChanged);
+ connect(m_handleSize, doubleSignal, doubleChanged);
+ connect(m_handleLineWidth, doubleSignal, doubleChanged);
+ connect(m_handleColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_handleSelectionColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_keyframeSize, doubleSignal, doubleChanged);
+ connect(m_keyframeColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_keyframeSelectionColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_curveWidth, doubleSignal, doubleChanged);
+ connect(m_curveColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_curveSelectionColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_treeMargins, doubleSignal, doubleChanged);
+ connect(m_playheadWidth, doubleSignal, doubleChanged);
+ connect(m_playheadRadius, doubleSignal, doubleChanged);
+ connect(m_playheadColor, &ColorControl::valueChanged, colorChanged);
+
+ auto *box = new QVBoxLayout;
+ box->addLayout(createRow("Background Color", m_background));
+ box->addLayout(createRow("Alternate Background Color", m_backgroundAlternate));
+ box->addLayout(createRow("Font Color", m_fontColor));
+ box->addLayout(createRow("Grid Color", m_gridColor));
+ box->addLayout(createRow("Canvas Margin", m_canvasMargin));
+ box->addLayout(createRow("Zoom In Width", m_zoomInWidth));
+ box->addLayout(createRow("Zoom In Height", m_zoomInHeight));
+ box->addLayout(createRow("Time Axis Height", m_timeAxisHeight));
+ box->addLayout(createRow("Time Axis Left Offset", m_timeOffsetLeft));
+ box->addLayout(createRow("Time Axis Right Offset", m_timeOffsetRight));
+ box->addLayout(createRow("Range Bar Color", m_rangeBarColor));
+ box->addLayout(createRow("Range Bar Caps Color", m_rangeBarCapsColor));
+ box->addLayout(createRow("Value Axis Width", m_valueAxisWidth));
+ box->addLayout(createRow("Value Axis Top Offset", m_valueOffsetTop));
+ box->addLayout(createRow("Value Axis Bottom Offset", m_valueOffsetBottom));
+ box->addLayout(createRow("Handle Size", m_handleSize));
+ box->addLayout(createRow("Handle Line Width", m_handleLineWidth));
+ box->addLayout(createRow("Handle Color", m_handleColor));
+ box->addLayout(createRow("Handle Selection Color", m_handleSelectionColor));
+ box->addLayout(createRow("Keyframe Size", m_keyframeSize));
+ box->addLayout(createRow("Keyframe Color", m_keyframeColor));
+ box->addLayout(createRow("Keyframe Selection Color", m_keyframeSelectionColor));
+ box->addLayout(createRow("Curve Width", m_curveWidth));
+ box->addLayout(createRow("Curve Color", m_curveColor));
+ box->addLayout(createRow("Curve Selection Color", m_curveSelectionColor));
+ box->addLayout(createRow("Treeview margins", m_treeMargins));
+ box->addLayout(createRow("Playhead width", m_playheadWidth));
+ box->addLayout(createRow("Playhead radius", m_playheadRadius));
+ box->addLayout(createRow("Playhead color", m_playheadColor));
+
+ box->addWidget(m_printButton);
+ setLayout(box);
+}
+
+CurveEditorStyle CurveEditorStyleDialog::style() const
+{
+ CurveEditorStyle style;
+ style.backgroundBrush = QBrush(m_background->value());
+ style.backgroundAlternateBrush = QBrush(m_backgroundAlternate->value());
+ style.fontColor = m_fontColor->value();
+ style.gridColor = m_gridColor->value();
+ style.canvasMargin = m_canvasMargin->value();
+ style.zoomInWidth = m_zoomInWidth->value();
+ style.zoomInHeight = m_zoomInHeight->value();
+ style.timeAxisHeight = m_timeAxisHeight->value();
+ style.timeOffsetLeft = m_timeOffsetLeft->value();
+ style.timeOffsetRight = m_timeOffsetRight->value();
+ style.rangeBarColor = m_rangeBarColor->value();
+ style.rangeBarCapsColor = m_rangeBarCapsColor->value();
+ style.valueAxisWidth = m_valueAxisWidth->value();
+ style.valueOffsetTop = m_valueOffsetTop->value();
+ style.valueOffsetBottom = m_valueOffsetBottom->value();
+ style.handleStyle.size = m_handleSize->value();
+ style.handleStyle.lineWidth = m_handleLineWidth->value();
+ style.handleStyle.color = m_handleColor->value();
+ style.handleStyle.selectionColor = m_handleSelectionColor->value();
+ style.keyframeStyle.size = m_keyframeSize->value();
+ style.keyframeStyle.color = m_keyframeColor->value();
+ style.keyframeStyle.selectionColor = m_keyframeSelectionColor->value();
+ style.curveStyle.width = m_curveWidth->value();
+ style.curveStyle.color = m_curveColor->value();
+ style.curveStyle.selectionColor = m_curveSelectionColor->value();
+ style.treeItemStyle.margins = m_treeMargins->value();
+ style.playhead.width = m_playheadWidth->value();
+ style.playhead.radius = m_playheadRadius->value();
+ style.playhead.color = m_playheadColor->value();
+
+ return style;
+}
+
+void CurveEditorStyleDialog::emitStyleChanged()
+{
+ emit styleChanged(style());
+}
+
+void CurveEditorStyleDialog::printStyle()
+{
+ auto toString = [](const QColor &color) {
+ QString tmp
+ = QString("QColor(%1, %2, %3)").arg(color.red()).arg(color.green()).arg(color.blue());
+ return qPrintable(tmp);
+ };
+
+ CurveEditorStyle s = style();
+ qDebug() << "";
+ qDebug().nospace() << "CurveEditorStyle out;";
+ qDebug().nospace() << "out.backgroundBrush = QBrush(" << toString(s.backgroundBrush.color())
+ << ");";
+ qDebug().nospace() << "out.backgroundAlternateBrush = QBrush("
+ << toString(s.backgroundAlternateBrush.color()) << ");";
+ qDebug().nospace() << "out.fontColor = " << toString(s.fontColor) << ";";
+ qDebug().nospace() << "out.gridColor = " << toString(s.gridColor) << ";";
+ qDebug().nospace() << "out.canvasMargin = " << s.canvasMargin << ";";
+ qDebug().nospace() << "out.zoomInWidth = " << s.zoomInWidth << ";";
+ qDebug().nospace() << "out.zoomInHeight = " << s.zoomInHeight << ";";
+ qDebug().nospace() << "out.timeAxisHeight = " << s.timeAxisHeight << ";";
+ qDebug().nospace() << "out.timeOffsetLeft = " << s.timeOffsetLeft << ";";
+ qDebug().nospace() << "out.timeOffsetRight = " << s.timeOffsetRight << ";";
+ qDebug().nospace() << "out.rangeBarColor = " << toString(s.rangeBarColor) << ";";
+ qDebug().nospace() << "out.rangeBarCapsColor = " << toString(s.rangeBarCapsColor) << ";";
+ qDebug().nospace() << "out.valueAxisWidth = " << s.valueAxisWidth << ";";
+ qDebug().nospace() << "out.valueOffsetTop = " << s.valueOffsetTop << ";";
+ qDebug().nospace() << "out.valueOffsetBottom = " << s.valueOffsetBottom << ";";
+ qDebug().nospace() << "out.handleStyle.size = " << s.handleStyle.size << ";";
+ qDebug().nospace() << "out.handleStyle.lineWidth = " << s.handleStyle.lineWidth << ";";
+ qDebug().nospace() << "out.handleStyle.color = " << toString(s.handleStyle.color) << ";";
+ qDebug().nospace() << "out.handleStyle.selectionColor = "
+ << toString(s.handleStyle.selectionColor) << ";";
+ qDebug().nospace() << "out.keyframeStyle.size = " << s.keyframeStyle.size << ";";
+ qDebug().nospace() << "out.keyframeStyle.color = " << toString(s.keyframeStyle.color) << ";";
+ qDebug().nospace() << "out.keyframeStyle.selectionColor = "
+ << toString(s.keyframeStyle.selectionColor) << ";";
+ qDebug().nospace() << "out.curveStyle.width = " << s.curveStyle.width << ";";
+ qDebug().nospace() << "out.curveStyle.color = " << toString(s.curveStyle.color) << ";";
+ qDebug().nospace() << "out.curveStyle.selectionColor = "
+ << toString(s.curveStyle.selectionColor) << ";";
+ qDebug().nospace() << "out.treeItemStyle.margins = " << s.treeItemStyle.margins << ";";
+ qDebug().nospace() << "out.playheadStyle.width = " << s.playhead.width << ";";
+ qDebug().nospace() << "out.playheadStyle.radius = " << s.playhead.radius << ";";
+ qDebug().nospace() << "out.playheadStyle.color = " << toString(s.playhead.color) << ";";
+ qDebug().nospace() << "return out;";
+ qDebug() << "";
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.h b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.h
new file mode 100644
index 0000000000..f1dc3bc372
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveeditorstyledialog.h
@@ -0,0 +1,127 @@
+
+/****************************************************************************
+**
+** 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 <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QPushButton;
+class QSpinBox;
+class QDoubleSpinBox;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+class ColorControl;
+
+struct CurveEditorStyle;
+
+class CurveEditorStyleDialog : public QDialog
+{
+ Q_OBJECT
+
+signals:
+ void styleChanged(const CurveEditorStyle &style);
+
+public:
+ CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent = nullptr);
+
+ CurveEditorStyle style() const;
+
+private:
+ void emitStyleChanged();
+
+ void printStyle();
+
+private:
+ QPushButton *m_printButton;
+
+ ColorControl *m_background;
+
+ ColorControl *m_backgroundAlternate;
+
+ ColorControl *m_fontColor;
+
+ ColorControl *m_gridColor;
+
+ QDoubleSpinBox *m_canvasMargin;
+
+ QSpinBox *m_zoomInWidth;
+
+ QSpinBox *m_zoomInHeight;
+
+ QDoubleSpinBox *m_timeAxisHeight;
+
+ QDoubleSpinBox *m_timeOffsetLeft;
+
+ QDoubleSpinBox *m_timeOffsetRight;
+
+ ColorControl *m_rangeBarColor;
+
+ ColorControl *m_rangeBarCapsColor;
+
+ QDoubleSpinBox *m_valueAxisWidth;
+
+ QDoubleSpinBox *m_valueOffsetTop;
+
+ QDoubleSpinBox *m_valueOffsetBottom;
+
+ // HandleItem
+ QDoubleSpinBox *m_handleSize;
+
+ QDoubleSpinBox *m_handleLineWidth;
+
+ ColorControl *m_handleColor;
+
+ ColorControl *m_handleSelectionColor;
+
+ // KeyframeItem
+ QDoubleSpinBox *m_keyframeSize;
+
+ ColorControl *m_keyframeColor;
+
+ ColorControl *m_keyframeSelectionColor;
+
+ // CurveItem
+ QDoubleSpinBox *m_curveWidth;
+
+ ColorControl *m_curveColor;
+
+ ColorControl *m_curveSelectionColor;
+
+ // TreeItem
+ QDoubleSpinBox *m_treeMargins;
+
+ // Playhead
+ QDoubleSpinBox *m_playheadWidth;
+
+ QDoubleSpinBox *m_playheadRadius;
+
+ ColorControl *m_playheadColor;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp
new file mode 100644
index 0000000000..38efd29571
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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 "curveitem.h"
+#include "animationcurve.h"
+#include "graphicsscene.h"
+#include "keyframeitem.h"
+#include "utils.h"
+
+#include <QPainter>
+#include <QPainterPath>
+
+#include <cmath>
+
+namespace DesignTools {
+
+CurveItem::CurveItem(QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_id(0)
+ , m_style()
+ , m_transform()
+ , m_keyframes()
+ , m_underMouse(false)
+ , m_itemDirty(false)
+ , m_pathDirty(true)
+{}
+
+CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_id(id)
+ , m_style()
+ , m_transform()
+ , m_keyframes()
+ , m_underMouse(false)
+ , m_itemDirty(false)
+ , m_pathDirty(true)
+{
+ setAcceptHoverEvents(true);
+
+ setFlag(QGraphicsItem::ItemIsMovable, false);
+
+ auto emitCurveChanged = [this]() {
+ m_itemDirty = true;
+ m_pathDirty = true;
+ update();
+ };
+
+ for (auto frame : curve.keyframes()) {
+ auto *item = new KeyframeItem(frame, this);
+ QObject::connect(item, &KeyframeItem::redrawCurve, emitCurveChanged);
+ m_keyframes.push_back(item);
+ }
+}
+
+CurveItem::~CurveItem() {}
+
+int CurveItem::type() const
+{
+ return Type;
+}
+
+QRectF CurveItem::boundingRect() const
+{
+ auto bbox = [](QRectF &bounds, const Keyframe &frame) {
+ grow(bounds, frame.position());
+ grow(bounds, frame.leftHandle());
+ grow(bounds, frame.rightHandle());
+ };
+
+ QRectF bounds;
+ for (auto *item : m_keyframes)
+ bbox(bounds, item->keyframe());
+
+ return m_transform.mapRect(bounds);
+}
+
+bool CurveItem::contains(const QPointF &point) const
+{
+ bool valid = false;
+ QPointF transformed(m_transform.inverted(&valid).map(point));
+
+ double width = std::abs(20.0 / scaleY(m_transform));
+
+ if (valid)
+ return curve().intersects(transformed, width);
+
+ return false;
+}
+
+void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ if (m_keyframes.size() > 1) {
+ QPen pen = painter->pen();
+ QColor col = m_underMouse ? Qt::red : m_style.color;
+
+ pen.setWidthF(m_style.width);
+ pen.setColor(hasSelection() ? m_style.selectionColor : col);
+
+ painter->save();
+ painter->setPen(pen);
+ painter->drawPath(path());
+
+ painter->restore();
+ }
+}
+
+bool CurveItem::isDirty() const
+{
+ return m_itemDirty;
+}
+
+bool CurveItem::hasSelection() const
+{
+ for (auto *frame : m_keyframes) {
+ if (frame->selected())
+ return true;
+ }
+
+ return false;
+}
+
+unsigned int CurveItem::id() const
+{
+ return m_id;
+}
+
+QPainterPath CurveItem::path() const
+{
+ if (m_pathDirty) {
+ Keyframe previous = m_keyframes.front()->keyframe();
+ Keyframe current;
+
+ m_path = QPainterPath(m_transform.map(previous.position()));
+ for (size_t i = 1; i < m_keyframes.size(); ++i) {
+ current = m_keyframes[i]->keyframe();
+
+ 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
+{
+ std::vector<Keyframe> out;
+ out.reserve(m_keyframes.size());
+ for (auto item : m_keyframes)
+ out.push_back(item->keyframe());
+
+ return out;
+}
+
+void CurveItem::setDirty(bool dirty)
+{
+ m_itemDirty = dirty;
+}
+
+QRectF CurveItem::setComponentTransform(const QTransform &transform)
+{
+ m_pathDirty = true;
+
+ prepareGeometryChange();
+ m_transform = transform;
+ for (auto frame : m_keyframes)
+ frame->setComponentTransform(transform);
+
+ return boundingRect();
+}
+
+void CurveItem::setStyle(const CurveEditorStyle &style)
+{
+ m_style = style.curveStyle;
+
+ for (auto *frame : m_keyframes)
+ frame->setStyle(style);
+}
+
+void CurveItem::connect(GraphicsScene *scene)
+{
+ for (auto *frame : m_keyframes) {
+ QObject::connect(frame, &KeyframeItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
+ QObject::connect(frame, &KeyframeItem::handleMoved, scene, &GraphicsScene::handleMoved);
+ }
+}
+
+void CurveItem::setIsUnderMouse(bool under)
+{
+ if (under != m_underMouse) {
+ m_underMouse = under;
+ update();
+ }
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h
new file mode 100644
index 0000000000..90e68e20f1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curveitem.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+#include "selectableitem.h"
+
+#include <QGraphicsObject>
+
+namespace DesignTools {
+
+class AnimationCurve;
+class KeyframeItem;
+class GraphicsScene;
+
+class CurveItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ CurveItem(QGraphicsItem *parent = nullptr);
+
+ CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem *parent = nullptr);
+
+ ~CurveItem() override;
+
+ enum { Type = ItemTypeCurve };
+
+ int type() const override;
+
+ QRectF boundingRect() const override;
+
+ bool contains(const QPointF &point) const override;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ bool isDirty() const;
+
+ bool hasSelection() const;
+
+ unsigned int id() const;
+
+ AnimationCurve curve() const;
+
+ void setDirty(bool dirty);
+
+ QRectF setComponentTransform(const QTransform &transform);
+
+ void setStyle(const CurveEditorStyle &style);
+
+ void connect(GraphicsScene *scene);
+
+ void setIsUnderMouse(bool under);
+
+private:
+ QPainterPath path() const;
+
+ unsigned int m_id;
+
+ CurveItemStyleOption m_style;
+
+ QTransform m_transform;
+
+ std::vector<KeyframeItem *> m_keyframes;
+
+ bool m_underMouse;
+
+ bool m_itemDirty;
+
+ mutable bool m_pathDirty;
+
+ mutable QPainterPath m_path;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.cpp
new file mode 100644
index 0000000000..40f675f3ec
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** 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.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.h b/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.h
new file mode 100644
index 0000000000..5dbce58bcc
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/curvesegment.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 "keyframe.h"
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+class QPointF;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+class CurveSegment
+{
+public:
+ CurveSegment();
+
+ CurveSegment(const Keyframe &first, const Keyframe &last);
+
+ bool containsX(double x) const;
+
+ QPointF evaluate(double t) const;
+
+ std::vector<QPointF> extrema() const;
+
+ std::vector<double> yForX(double x) const;
+
+ std::vector<double> xForY(double y) const;
+
+ void setLeft(const Keyframe &frame);
+
+ void setRight(const Keyframe &frame);
+
+private:
+ Keyframe m_left;
+
+ Keyframe m_right;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp
new file mode 100644
index 0000000000..096e57aafa
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.cpp
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** 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 "graphicsscene.h"
+#include "animationcurve.h"
+#include "curveitem.h"
+#include "graphicsview.h"
+#include "handleitem.h"
+
+#include <QGraphicsSceneMouseEvent>
+
+namespace DesignTools {
+
+GraphicsScene::GraphicsScene(QObject *parent)
+ : QGraphicsScene(parent)
+ , m_dirty(true)
+ , m_limits()
+{}
+
+bool GraphicsScene::empty() const
+{
+ return items().empty();
+}
+
+double GraphicsScene::minimumTime() const
+{
+ return limits().left();
+}
+
+double GraphicsScene::maximumTime() const
+{
+ return limits().right();
+}
+
+double GraphicsScene::minimumValue() const
+{
+ return limits().bottom();
+}
+
+double GraphicsScene::maximumValue() const
+{
+ return limits().top();
+}
+
+void GraphicsScene::addCurveItem(CurveItem *item)
+{
+ m_dirty = true;
+ addItem(item);
+ item->connect(this);
+}
+
+void GraphicsScene::setComponentTransform(const QTransform &transform)
+{
+ QRectF bounds;
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
+ bounds = bounds.united(curveItem->setComponentTransform(transform));
+ }
+
+ if (bounds.isNull()) {
+ if (GraphicsView *gview = graphicsView())
+ bounds = gview->defaultRasterRect();
+ }
+
+ if (bounds.isValid())
+ setSceneRect(bounds);
+}
+
+void GraphicsScene::keyframeMoved(KeyframeItem *movedItem, const QPointF &direction)
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (item == movedItem)
+ continue;
+
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (frameItem->selected())
+ frameItem->moveKeyframe(direction);
+ }
+ }
+}
+
+void GraphicsScene::handleMoved(KeyframeItem *frame,
+ HandleSlot handle,
+ double angle,
+ double deltaLength)
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (item == frame)
+ continue;
+
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (frameItem->selected())
+ frameItem->moveHandle(handle, angle, deltaLength);
+ }
+ }
+}
+
+void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ QGraphicsScene::mouseMoveEvent(mouseEvent);
+
+ if (hasActiveItem())
+ return;
+
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
+ curveItem->setIsUnderMouse(curveItem->contains(mouseEvent->scenePos()));
+ }
+}
+
+void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ QGraphicsScene::mouseReleaseEvent(mouseEvent);
+
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
+ if (curveItem->contains(mouseEvent->scenePos()))
+ curveItem->setSelected(true);
+
+ if (curveItem->isDirty()) {
+ emit curveChanged(curveItem->id(), curveItem->curve());
+ curveItem->setDirty(false);
+ m_dirty = true;
+ }
+ }
+ }
+}
+
+bool GraphicsScene::hasActiveKeyframe() const
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *kitem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (kitem->activated())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicsScene::hasActiveHandle() const
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *hitem = qgraphicsitem_cast<HandleItem *>(item)) {
+ if (hitem->activated())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicsScene::hasActiveItem() const
+{
+ return hasActiveKeyframe() || hasActiveHandle();
+}
+
+GraphicsView *GraphicsScene::graphicsView() const
+{
+ const QList<QGraphicsView *> viewList = views();
+ for (auto &&view : viewList) {
+ if (GraphicsView *gview = qobject_cast<GraphicsView *>(view))
+ return gview;
+ }
+ return nullptr;
+}
+
+QRectF GraphicsScene::limits() const
+{
+ if (m_dirty) {
+ QPointF min(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
+ QPointF max(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest());
+
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
+ auto curve = curveItem->curve();
+ if (min.x() > curve.minimumTime())
+ min.rx() = curve.minimumTime();
+
+ if (min.y() > curve.minimumValue())
+ min.ry() = curve.minimumValue();
+
+ if (max.x() < curve.maximumTime())
+ max.rx() = curve.maximumTime();
+
+ if (max.y() < curve.maximumValue())
+ max.ry() = curve.maximumValue();
+ }
+ }
+
+ m_limits = QRectF(QPointF(min.x(), max.y()), QPointF(max.x(), min.y()));
+ m_dirty = false;
+ }
+ return m_limits;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h
new file mode 100644
index 0000000000..981c326b5a
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsscene.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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 "keyframeitem.h"
+
+#include <QGraphicsScene>
+
+namespace DesignTools {
+
+class AnimationCurve;
+class CurveItem;
+class GraphicsView;
+
+class GraphicsScene : public QGraphicsScene
+{
+ Q_OBJECT
+
+signals:
+ void curveChanged(unsigned int id, const AnimationCurve &curve);
+
+public:
+ GraphicsScene(QObject *parent = nullptr);
+
+ bool empty() const;
+
+ bool hasActiveKeyframe() const;
+
+ bool hasActiveHandle() const;
+
+ bool hasActiveItem() const;
+
+ double minimumTime() const;
+
+ double maximumTime() const;
+
+ double minimumValue() const;
+
+ double maximumValue() const;
+
+ void addCurveItem(CurveItem *item);
+
+ void setComponentTransform(const QTransform &transform);
+
+ void keyframeMoved(KeyframeItem *item, const QPointF &direction);
+
+ void handleMoved(KeyframeItem *frame, HandleSlot handle, double angle, double deltaLength);
+
+protected:
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
+
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
+
+private:
+ using QGraphicsScene::addItem;
+
+ GraphicsView *graphicsView() const;
+
+ QRectF limits() const;
+
+ mutable bool m_dirty;
+
+ mutable QRectF m_limits;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp
new file mode 100644
index 0000000000..ae60888d1f
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp
@@ -0,0 +1,523 @@
+/****************************************************************************
+**
+** 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 "graphicsview.h"
+#include "curveeditormodel.h"
+#include "curveitem.h"
+#include "utils.h"
+
+#include <QAction>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QScrollBar>
+
+#include <cmath>
+
+namespace DesignTools {
+
+GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
+ : QGraphicsView(parent)
+ , m_zoomX(0.0)
+ , m_zoomY(0.0)
+ , m_transform()
+ , m_scene()
+ , m_model(model)
+ , m_playhead(this)
+ , m_selector()
+ , m_style(model->style())
+ , m_dialog(m_style)
+{
+ model->setGraphicsView(this);
+
+ setScene(&m_scene);
+ setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ setResizeAnchor(QGraphicsView::NoAnchor);
+ setTransformationAnchor(QGraphicsView::NoAnchor);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
+
+ connect(&m_dialog, &CurveEditorStyleDialog::styleChanged, this, &GraphicsView::setStyle);
+
+ auto itemSlot = [this](unsigned int id, const AnimationCurve &curve) {
+ applyZoom(m_zoomX, m_zoomY);
+ m_model->setCurve(id, curve);
+ };
+
+ connect(&m_scene, &GraphicsScene::curveChanged, itemSlot);
+
+ applyZoom(m_zoomX, m_zoomY);
+ update();
+}
+
+CurveEditorModel *GraphicsView::model() const
+{
+ return m_model;
+}
+
+CurveEditorStyle GraphicsView::editorStyle() const
+{
+ return m_style;
+}
+
+bool GraphicsView::hasActiveItem() const
+{
+ return m_scene.hasActiveItem();
+}
+
+bool GraphicsView::hasActiveHandle() const
+{
+ return m_scene.hasActiveHandle();
+}
+
+double GraphicsView::minimumTime() const
+{
+ bool check = m_model->minimumTime() < m_scene.minimumTime();
+ return check ? m_model->minimumTime() : m_scene.minimumTime();
+}
+
+double GraphicsView::maximumTime() const
+{
+ bool check = m_model->maximumTime() > m_scene.maximumTime();
+ return check ? m_model->maximumTime() : m_scene.maximumTime();
+}
+
+double GraphicsView::minimumValue() const
+{
+ return m_scene.empty() ? -1.0 : m_scene.minimumValue();
+}
+
+double GraphicsView::maximumValue() const
+{
+ return m_scene.empty() ? 1.0 : m_scene.maximumValue();
+}
+
+double GraphicsView::zoomX() const
+{
+ return m_zoomX;
+}
+
+double GraphicsView::zoomY() const
+{
+ return m_zoomY;
+}
+
+QRectF GraphicsView::canvasRect() const
+{
+ QRect r = viewport()->rect().adjusted(
+ m_style.valueAxisWidth + m_style.canvasMargin,
+ m_style.timeAxisHeight + m_style.canvasMargin,
+ -m_style.canvasMargin,
+ -m_style.canvasMargin);
+
+ return mapToScene(r).boundingRect();
+}
+
+QRectF GraphicsView::timeScaleRect() const
+{
+ QRect vp(viewport()->rect());
+ QPoint tl = vp.topLeft() + QPoint(m_style.valueAxisWidth, 0);
+ QPoint br = vp.topRight() + QPoint(0, m_style.timeAxisHeight);
+ return mapToScene(QRect(tl, br)).boundingRect();
+}
+
+QRectF GraphicsView::valueScaleRect() const
+{
+ QRect vp(viewport()->rect());
+ QPoint tl = vp.topLeft() + QPoint(0, m_style.timeAxisHeight);
+ QPoint br = vp.bottomLeft() + QPoint(m_style.valueAxisWidth, 0);
+ return mapToScene(QRect(tl, br)).boundingRect();
+}
+
+QRectF GraphicsView::defaultRasterRect() const
+{
+ QPointF topLeft(mapTimeToX(minimumTime()), mapValueToY(maximumValue()));
+ QPointF bottomRight(mapTimeToX(maximumTime()), mapValueToY(minimumValue()));
+ return QRectF(topLeft, bottomRight);
+}
+
+void GraphicsView::setStyle(const CurveEditorStyle &style)
+{
+ m_style = style;
+
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
+ curveItem->setStyle(style);
+ }
+
+ applyZoom(m_zoomX, m_zoomY);
+ viewport()->update();
+}
+
+void GraphicsView::setZoomX(double zoom, const QPoint &pivot)
+{
+ applyZoom(zoom, m_zoomY, pivot);
+ viewport()->update();
+}
+
+void GraphicsView::setZoomY(double zoom, const QPoint &pivot)
+{
+ applyZoom(m_zoomX, zoom, pivot);
+ viewport()->update();
+}
+
+void GraphicsView::setCurrentFrame(int frame)
+{
+ int clampedFrame = clamp(frame, m_model->minimumTime(), m_model->maximumTime());
+ m_playhead.moveToFrame(clampedFrame, this);
+ viewport()->update();
+}
+
+void GraphicsView::scrollContent(double x, double y)
+{
+ QScrollBar *hs = horizontalScrollBar();
+ QScrollBar *vs = verticalScrollBar();
+ hs->setValue(hs->value() + x);
+ vs->setValue(vs->value() + y);
+}
+
+void GraphicsView::reset(const std::vector<CurveItem *> &items)
+{
+ m_scene.clear();
+ for (auto *item : items)
+ m_scene.addCurveItem(item);
+
+ applyZoom(m_zoomX, m_zoomY);
+ viewport()->update();
+}
+
+void GraphicsView::resizeEvent(QResizeEvent *event)
+{
+ QGraphicsView::resizeEvent(event);
+ applyZoom(m_zoomX, m_zoomY);
+}
+
+void GraphicsView::keyPressEvent(QKeyEvent *event)
+{
+ Shortcut shortcut(event->modifiers(), static_cast<Qt::Key>(event->key()));
+ if (shortcut == m_style.shortcuts.frameAll)
+ applyZoom(0.0, 0.0);
+}
+
+void GraphicsView::mousePressEvent(QMouseEvent *event)
+{
+ if (m_playhead.mousePress(globalToScene(event->globalPos())))
+ return;
+
+ Shortcut shortcut(event);
+ if (shortcut == Shortcut(Qt::LeftButton)) {
+ QPointF pos = mapToScene(event->pos());
+ if (timeScaleRect().contains(pos)) {
+ setCurrentFrame(std::round(mapXtoTime(pos.x())));
+ event->accept();
+ return;
+ }
+ }
+
+ QGraphicsView::mousePressEvent(event);
+
+ m_selector.mousePress(event, this);
+}
+
+void GraphicsView::mouseMoveEvent(QMouseEvent *event)
+{
+ if (m_playhead.mouseMove(globalToScene(event->globalPos()), this))
+ return;
+
+ QGraphicsView::mouseMoveEvent(event);
+
+ m_selector.mouseMove(event, this, m_playhead);
+}
+
+void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
+{
+ QGraphicsView::mouseReleaseEvent(event);
+
+ m_playhead.mouseRelease(this);
+ m_selector.mouseRelease(event, this);
+ this->viewport()->update();
+}
+
+void GraphicsView::wheelEvent(QWheelEvent *event)
+{
+ if (event->modifiers().testFlag(Qt::AltModifier))
+ return;
+
+ QGraphicsView::wheelEvent(event);
+}
+
+void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
+{
+ if (event->modifiers() != Qt::NoModifier)
+ return;
+
+ auto openStyleEditor = [this]() { m_dialog.show(); };
+
+ QMenu menu;
+ QAction *openEditorAction = menu.addAction(tr("Open Style Editor"));
+ connect(openEditorAction, &QAction::triggered, openStyleEditor);
+
+ menu.exec(event->globalPos());
+}
+
+void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
+{
+ QRectF abscissa = timeScaleRect();
+ if (abscissa.isValid())
+ drawTimeScale(painter, abscissa);
+
+ auto ordinate = valueScaleRect();
+ if (ordinate.isValid())
+ drawValueScale(painter, ordinate);
+
+ m_playhead.paint(painter, this);
+
+ painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()),
+ m_style.backgroundAlternateBrush);
+
+ m_selector.paint(painter);
+}
+
+void GraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
+{
+ painter->fillRect(rect, m_style.backgroundBrush);
+ painter->fillRect(scene()->sceneRect(), m_style.backgroundAlternateBrush);
+
+ drawGrid(painter, rect);
+ drawExtremaX(painter, rect);
+ drawExtremaY(painter, rect);
+}
+
+int GraphicsView::mapTimeToX(double time) const
+{
+ return std::round(time * scaleX(m_transform));
+}
+
+int GraphicsView::mapValueToY(double y) const
+{
+ return std::round(y * scaleY(m_transform));
+}
+
+double GraphicsView::mapXtoTime(int x) const
+{
+ return static_cast<double>(x) / scaleX(m_transform);
+}
+
+double GraphicsView::mapYtoValue(int y) const
+{
+ return static_cast<double>(y) / scaleY(m_transform);
+}
+
+QPointF GraphicsView::globalToScene(const QPoint &point) const
+{
+ return mapToScene(viewport()->mapFromGlobal(point));
+}
+
+QPointF GraphicsView::globalToRaster(const QPoint &point) const
+{
+ QPointF scene = globalToScene(point);
+ return QPointF(mapXtoTime(scene.x()), mapYtoValue(scene.y()));
+}
+
+void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
+{
+ QPointF pivotRaster(globalToRaster(pivot));
+
+ m_zoomX = clamp(x, 0.0, 1.0);
+ m_zoomY = clamp(y, 0.0, 1.0);
+
+ double minTime = minimumTime();
+ double maxTime = maximumTime();
+
+ double minValue = minimumValue();
+ double maxValue = maximumValue();
+
+ QRectF canvas = canvasRect();
+
+ double xZoomedOut = canvas.width() / (maxTime - minTime);
+ double xZoomedIn = m_style.zoomInWidth;
+ double scaleX = lerp(clamp(m_zoomX, 0.0, 1.0), xZoomedOut, xZoomedIn);
+
+ double yZoomedOut = canvas.height() / (maxValue - minValue);
+ double yZoomedIn = m_style.zoomInHeight;
+ double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn);
+
+ m_transform = QTransform::fromScale(scaleX, scaleY);
+
+ m_scene.setComponentTransform(m_transform);
+
+ QRectF sr = m_scene.sceneRect().adjusted(
+ -m_style.valueAxisWidth - m_style.canvasMargin,
+ -m_style.timeAxisHeight - m_style.canvasMargin,
+ m_style.canvasMargin,
+ m_style.canvasMargin);
+
+ setSceneRect(sr);
+
+ m_playhead.resize(this);
+
+ if (!pivot.isNull()) {
+ QPointF deltaTransformed = pivotRaster - globalToRaster(pivot);
+ scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
+ }
+}
+
+void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
+{
+ QRectF gridRect = rect.adjusted(
+ m_style.valueAxisWidth + m_style.canvasMargin,
+ m_style.timeAxisHeight + m_style.canvasMargin,
+ -m_style.canvasMargin,
+ -m_style.canvasMargin);
+
+ if (!gridRect.isValid())
+ return;
+
+ auto drawVerticalLine = [painter, gridRect](double position) {
+ painter->drawLine(position, gridRect.top(), position, gridRect.bottom());
+ };
+
+ painter->save();
+ painter->setPen(m_style.gridColor);
+
+ double timeIncrement = timeLabelInterval(painter, m_model->maximumTime());
+ for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
+ drawVerticalLine(mapTimeToX(i));
+
+ painter->restore();
+}
+
+void GraphicsView::drawExtremaX(QPainter *painter, const QRectF &rect)
+{
+ auto drawVerticalLine = [rect, painter](double position) {
+ painter->drawLine(position, rect.top(), position, rect.bottom());
+ };
+
+ painter->save();
+ painter->setPen(Qt::red);
+ drawVerticalLine(mapTimeToX(m_model->minimumTime()));
+ drawVerticalLine(mapTimeToX(m_model->maximumTime()));
+ painter->restore();
+}
+
+void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
+{
+ if (m_scene.empty())
+ return;
+
+ auto drawHorizontalLine = [rect, painter](double position) {
+ painter->drawLine(rect.left(), position, rect.right(), position);
+ };
+
+ painter->save();
+ painter->setPen(Qt::blue);
+ drawHorizontalLine(mapValueToY(m_scene.minimumValue()));
+ drawHorizontalLine(mapValueToY(m_scene.maximumValue()));
+
+ drawHorizontalLine(mapValueToY(0.0));
+
+ painter->restore();
+}
+
+void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect)
+{
+ painter->save();
+ painter->setPen(m_style.fontColor);
+ painter->fillRect(rect, m_style.backgroundAlternateBrush);
+
+ QFontMetrics fm(painter->font());
+
+ auto paintLabeledTick = [this, painter, rect, fm](double time) {
+ QString timeText = QString("%1").arg(time);
+
+ int position = mapTimeToX(time);
+
+ QRect textRect = fm.boundingRect(timeText);
+ textRect.moveCenter(QPoint(position, rect.center().y()));
+
+ painter->drawText(textRect, Qt::AlignCenter, timeText);
+ painter->drawLine(position, rect.bottom() - 2, position, textRect.bottom() + 2);
+ };
+
+ double timeIncrement = timeLabelInterval(painter, maximumTime());
+ for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
+ paintLabeledTick(i);
+
+ painter->restore();
+}
+
+void GraphicsView::drawValueScale(QPainter *painter, const QRectF &rect)
+{
+ painter->save();
+ painter->setPen(m_style.fontColor);
+ painter->fillRect(rect, m_style.backgroundAlternateBrush);
+
+ QFontMetrics fm(painter->font());
+ auto paintLabeledTick = [this, painter, rect, fm](double value) {
+ QString valueText = QString("%1").arg(value);
+
+ int position = mapValueToY(value);
+
+ QRect textRect = fm.boundingRect(valueText);
+ textRect.moveCenter(QPoint(rect.center().x(), position));
+ painter->drawText(textRect, Qt::AlignCenter, valueText);
+ };
+
+ paintLabeledTick(minimumValue());
+ paintLabeledTick(maximumValue());
+ painter->restore();
+}
+
+double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
+{
+ QFontMetrics fm(painter->font());
+ int minTextSpacing = fm.width(QString("X%1X").arg(maxTime));
+
+ int deltaTime = 1;
+ int nextFactor = 5;
+
+ double tickDistance = mapTimeToX(deltaTime);
+
+ while (true) {
+ if (tickDistance == 0 && deltaTime > maxTime)
+ return maxTime;
+
+ if (tickDistance > minTextSpacing)
+ break;
+
+ deltaTime *= nextFactor;
+ tickDistance = mapTimeToX(deltaTime);
+
+ if (nextFactor == 5)
+ nextFactor = 2;
+ else
+ nextFactor = 5;
+ }
+
+ return deltaTime;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h
new file mode 100644
index 0000000000..1975e3696d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+#include "curveeditorstyledialog.h"
+#include "graphicsscene.h"
+#include "playhead.h"
+#include "selector.h"
+
+#include <QGraphicsView>
+
+namespace DesignTools {
+
+class CurveItem;
+class CurveEditorModel;
+class Playhead;
+
+class GraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+
+ friend class Playhead;
+
+public:
+ GraphicsView(CurveEditorModel *model, QWidget *parent = nullptr);
+
+ CurveEditorModel *model() const;
+
+ CurveEditorStyle editorStyle() const;
+
+ bool hasActiveItem() const;
+
+ bool hasActiveHandle() const;
+
+ int mapTimeToX(double time) const;
+
+ int mapValueToY(double value) const;
+
+ double mapXtoTime(int x) const;
+
+ double mapYtoValue(int y) const;
+
+ QPointF globalToScene(const QPoint &point) const;
+
+ QPointF globalToRaster(const QPoint &point) const;
+
+ double minimumTime() const;
+
+ double maximumTime() const;
+
+ double minimumValue() const;
+
+ double maximumValue() const;
+
+ double zoomX() const;
+
+ double zoomY() const;
+
+ QRectF canvasRect() const;
+
+ QRectF timeScaleRect() const;
+
+ QRectF valueScaleRect() const;
+
+ QRectF defaultRasterRect() const;
+
+ void setStyle(const CurveEditorStyle &style);
+
+ void setZoomX(double zoom, const QPoint &pivot = QPoint());
+
+ void setZoomY(double zoom, const QPoint &pivot = QPoint());
+
+ void setCurrentFrame(int frame);
+
+ void scrollContent(double x, double y);
+
+ void reset(const std::vector<CurveItem *> &items);
+
+protected:
+ void resizeEvent(QResizeEvent *event) override;
+
+ void keyPressEvent(QKeyEvent *event) override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+
+ void mouseMoveEvent(QMouseEvent *event) override;
+
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+ void wheelEvent(QWheelEvent *event) override;
+
+ void contextMenuEvent(QContextMenuEvent *event) override;
+
+ void drawForeground(QPainter *painter, const QRectF &rect) override;
+
+ void drawBackground(QPainter *painter, const QRectF &rect) override;
+
+private:
+ void applyZoom(double x, double y, const QPoint &pivot = QPoint());
+
+ void drawGrid(QPainter *painter, const QRectF &rect);
+
+ void drawExtremaX(QPainter *painter, const QRectF &rect);
+
+ void drawExtremaY(QPainter *painter, const QRectF &rect);
+
+ void drawTimeScale(QPainter *painter, const QRectF &rect);
+
+ void drawValueScale(QPainter *painter, const QRectF &rect);
+
+ double timeLabelInterval(QPainter *painter, double maxTime);
+
+private:
+ double m_zoomX;
+
+ double m_zoomY;
+
+ QTransform m_transform;
+
+ GraphicsScene m_scene;
+
+ CurveEditorModel *m_model;
+
+ Playhead m_playhead;
+
+ Selector m_selector;
+
+ CurveEditorStyle m_style;
+
+ CurveEditorStyleDialog m_dialog;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp
new file mode 100644
index 0000000000..c54b26e279
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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 "handleitem.h"
+#include "utils.h"
+
+#include <QPainter>
+
+namespace DesignTools {
+
+struct HandleGeometry
+{
+ HandleGeometry(const QPointF &pos, const HandleItemStyleOption &style)
+ {
+ QPointF topLeft(-style.size / 2.0, -style.size / 2.0);
+ handle = QRectF(topLeft, -topLeft);
+ toKeyframe = QLineF(QPointF(0.0, 0.0), -pos);
+ angle = -toKeyframe.angle() + 45.0;
+ }
+
+ QRectF handle;
+
+ QLineF toKeyframe;
+
+ double angle;
+};
+
+HandleItem::HandleItem(QGraphicsItem *parent)
+ : SelectableItem(parent)
+ , m_style()
+{
+ setFlag(QGraphicsItem::ItemStacksBehindParent, true);
+}
+
+HandleItem::~HandleItem() {}
+
+int HandleItem::type() const
+{
+ return Type;
+}
+
+QRectF HandleItem::boundingRect() const
+{
+ HandleGeometry geom(pos(), m_style);
+
+ QTransform transform;
+ transform.rotate(geom.angle);
+
+ QRectF bounds = bbox(geom.handle, transform);
+ grow(bounds, -pos());
+ return bounds;
+}
+
+void HandleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ QColor handleColor(isSelected() ? m_style.selectionColor : m_style.color);
+
+ HandleGeometry geom(pos(), m_style);
+
+ QPen pen = painter->pen();
+ pen.setWidthF(m_style.lineWidth);
+ pen.setColor(handleColor);
+
+ painter->save();
+ painter->setPen(pen);
+
+ painter->drawLine(geom.toKeyframe);
+ painter->rotate(geom.angle);
+ painter->fillRect(geom.handle, handleColor);
+
+ painter->restore();
+}
+
+void HandleItem::setStyle(const CurveEditorStyle &style)
+{
+ m_style = style.handleStyle;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.h
new file mode 100644
index 0000000000..4c9126c629
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+#include "selectableitem.h"
+
+namespace DesignTools {
+
+class HandleItem : public SelectableItem
+{
+ Q_OBJECT
+
+public:
+ HandleItem(QGraphicsItem *parent);
+
+ ~HandleItem() override;
+
+ enum { Type = ItemTypeHandle };
+
+ int type() const override;
+
+ QRectF boundingRect() const override;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ void setStyle(const CurveEditorStyle &style);
+
+private:
+ HandleItemStyleOption m_style;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
new file mode 100644
index 0000000000..94cdbbcbb1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** 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 "keyframeitem.h"
+#include "handleitem.h"
+
+#include <QPainter>
+
+#include <cmath>
+
+namespace DesignTools {
+
+KeyframeItem::KeyframeItem(QGraphicsItem *parent)
+ : SelectableItem(parent)
+ , m_frame()
+{}
+
+KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent)
+ : SelectableItem(parent)
+ , m_transform()
+ , m_frame(keyframe)
+ , m_left(keyframe.hasLeftHandle() ? new HandleItem(this) : nullptr)
+ , m_right(keyframe.hasRightHandle() ? new HandleItem(this) : nullptr)
+{
+ auto updatePosition = [this]() { this->updatePosition(true); };
+ 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
+{
+ return Type;
+}
+
+QRectF KeyframeItem::boundingRect() const
+{
+ QPointF topLeft(-m_style.size / 2.0, -m_style.size / 2.0);
+ return QRectF(topLeft, -topLeft);
+}
+
+void KeyframeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ QPen pen = painter->pen();
+ pen.setColor(Qt::black);
+
+ painter->save();
+ painter->setPen(pen);
+ painter->setBrush(selected() ? Qt::red : m_style.color);
+ painter->drawEllipse(boundingRect());
+
+ painter->restore();
+}
+
+KeyframeItem::~KeyframeItem() {}
+
+Keyframe KeyframeItem::keyframe() const
+{
+ return m_frame;
+}
+
+void KeyframeItem::setComponentTransform(const QTransform &transform)
+{
+ m_transform = transform;
+
+ if (m_left)
+ m_left->setPos(m_transform.map(m_frame.leftHandle() - m_frame.position()));
+
+ if (m_right)
+ m_right->setPos(m_transform.map(m_frame.rightHandle() - m_frame.position()));
+
+ setPos(m_transform.map(m_frame.position()));
+}
+
+void KeyframeItem::setStyle(const CurveEditorStyle &style)
+{
+ m_style = style.keyframeStyle;
+
+ if (m_left)
+ m_left->setStyle(style);
+
+ if (m_right)
+ m_right->setStyle(style);
+}
+
+void KeyframeItem::updatePosition(bool update)
+{
+ bool ok = false;
+ QPointF position = m_transform.inverted(&ok).map(pos());
+
+ if (!ok)
+ return;
+
+ QPointF oldPosition = m_frame.position();
+ m_frame.setPosition(position);
+
+ if (m_left)
+ updateHandle(m_left, false);
+
+ if (m_right)
+ updateHandle(m_right, false);
+
+ if (update) {
+ emit redrawCurve();
+ emit keyframeMoved(this, position - oldPosition);
+ }
+}
+
+void KeyframeItem::moveKeyframe(const QPointF &direction)
+{
+ this->blockSignals(true);
+ setPos(m_transform.map(m_frame.position() + direction));
+ updatePosition(false);
+ this->blockSignals(false);
+ emit redrawCurve();
+}
+
+void KeyframeItem::moveHandle(HandleSlot handle, double deltaAngle, double deltaLength)
+{
+ auto move = [this, deltaAngle, deltaLength](HandleItem *item) {
+ QLineF current(QPointF(0.0, 0.0), item->pos());
+ current.setAngle(current.angle() + deltaAngle);
+ current.setLength(current.length() + deltaLength);
+ item->setPos(current.p2());
+ updateHandle(item, false);
+ };
+
+ this->blockSignals(true);
+
+ if (handle == HandleSlot::Left)
+ move(m_left);
+ else if (handle == HandleSlot::Right)
+ move(m_right);
+
+ this->blockSignals(false);
+
+ emit redrawCurve();
+}
+
+void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged)
+{
+ bool ok = false;
+
+ QPointF handlePosition = m_transform.inverted(&ok).map(handle->pos());
+
+ if (!ok)
+ return;
+
+ QPointF oldPosition;
+ QPointF newPosition;
+ HandleSlot slot = HandleSlot::Undefined;
+ if (handle == m_left) {
+ slot = HandleSlot::Left;
+ oldPosition = m_frame.leftHandle();
+ m_frame.setLeftHandle(m_frame.position() + handlePosition);
+ newPosition = m_frame.leftHandle();
+ } else {
+ slot = HandleSlot::Right;
+ oldPosition = m_frame.rightHandle();
+ m_frame.setRightHandle(m_frame.position() + handlePosition);
+ newPosition = m_frame.rightHandle();
+ }
+
+ if (emitChanged) {
+ QLineF oldLine(m_frame.position(), oldPosition);
+ QLineF newLine(m_frame.position(), newPosition);
+
+ QLineF mappedOld = m_transform.map(oldLine);
+ QLineF mappedNew = m_transform.map(newLine);
+
+ auto angle = mappedOld.angleTo(mappedNew);
+ auto deltaLength = mappedNew.length() - mappedOld.length();
+
+ emit redrawCurve();
+ emit handleMoved(this, slot, angle, deltaLength);
+ }
+}
+
+QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
+{
+ if (change == ItemPositionChange) {
+ bool ok;
+ QPointF position = m_transform.inverted(&ok).map(value.toPointF());
+ if (ok) {
+ position.setX(std::round(position.x()));
+ return QVariant(m_transform.map(position));
+ }
+ }
+
+ return QGraphicsItem::itemChange(change, value);
+}
+
+void KeyframeItem::selectionCallback()
+{
+ auto setHandleVisibility = [](HandleItem *handle, bool visible) {
+ if (handle)
+ handle->setVisible(visible);
+ };
+
+ if (selected()) {
+ setHandleVisibility(m_left, true);
+ setHandleVisibility(m_right, true);
+ } else {
+ setHandleVisibility(m_left, false);
+ setHandleVisibility(m_right, false);
+ }
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h
new file mode 100644
index 0000000000..ae65be1e9b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+#include "keyframe.h"
+#include "selectableitem.h"
+
+#include <QGraphicsObject>
+
+namespace DesignTools {
+
+class HandleItem;
+
+enum class HandleSlot { Undefined, Left, Right };
+
+class KeyframeItem : public SelectableItem
+{
+ Q_OBJECT
+
+signals:
+ void redrawCurve();
+
+ void keyframeMoved(KeyframeItem *item, const QPointF &direction);
+
+ void handleMoved(KeyframeItem *frame, HandleSlot handle, double angle, double deltaLength);
+
+public:
+ KeyframeItem(QGraphicsItem *parent = nullptr);
+
+ KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent = nullptr);
+
+ ~KeyframeItem() override;
+
+ enum { Type = ItemTypeKeyframe };
+
+ int type() const override;
+
+ QRectF boundingRect() const override;
+
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
+
+ Keyframe keyframe() const;
+
+ void setComponentTransform(const QTransform &transform);
+
+ void setStyle(const CurveEditorStyle &style);
+
+ void moveKeyframe(const QPointF &direction);
+
+ void moveHandle(HandleSlot handle, double deltaAngle, double deltaLength);
+
+protected:
+ QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
+
+ void selectionCallback() override;
+
+private:
+ void updatePosition(bool emit = true);
+
+ void updateHandle(HandleItem *handle, bool emit = true);
+
+private:
+ QTransform m_transform;
+
+ KeyframeItemStyleOption m_style;
+
+ Keyframe m_frame;
+
+ HandleItem *m_left;
+
+ HandleItem *m_right;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp
new file mode 100644
index 0000000000..e2e6d21274
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** 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 "playhead.h"
+#include "curveeditormodel.h"
+#include "graphicsview.h"
+
+#include <QApplication>
+#include <QGraphicsScene>
+#include <QPainter>
+
+#include <cmath>
+
+namespace DesignTools {
+
+constexpr double g_playheadMargin = 5.0;
+
+Playhead::Playhead(GraphicsView *view)
+ : m_frame(0)
+ , m_moving(false)
+ , m_rect()
+ , m_timer()
+{
+ m_timer.setSingleShot(true);
+ m_timer.setInterval(30);
+ QObject::connect(&m_timer, &QTimer::timeout, view, [this, view]() {
+ if (QApplication::mouseButtons() == Qt::LeftButton)
+ mouseMoveOutOfBounds(view);
+ });
+}
+
+int Playhead::currentFrame() const
+{
+ return m_frame;
+}
+
+void Playhead::moveToFrame(int frame, GraphicsView *view)
+{
+ m_frame = frame;
+ m_rect.moveCenter(QPointF(view->mapTimeToX(m_frame), m_rect.center().y()));
+}
+
+void Playhead::resize(GraphicsView *view)
+{
+ QRectF viewRect = view->mapToScene(view->viewport()->rect()).boundingRect();
+
+ CurveEditorStyle style = view->editorStyle();
+
+ QPointF tlr(style.valueAxisWidth, style.timeAxisHeight - style.playhead.width);
+ QPointF brr(style.valueAxisWidth + style.playhead.width, -g_playheadMargin);
+
+ QPointF tl = viewRect.topLeft() + tlr;
+ QPointF br = viewRect.bottomLeft() + brr;
+
+ m_rect = QRectF(tl, br);
+
+ moveToFrame(m_frame, view);
+}
+
+bool Playhead::mousePress(const QPointF &pos)
+{
+ QRectF hitRect = m_rect;
+ hitRect.setBottom(hitRect.top() + hitRect.width());
+
+ m_moving = hitRect.contains(pos);
+
+ return m_moving;
+}
+
+bool Playhead::mouseMove(const QPointF &pos, GraphicsView *view)
+{
+ if (m_moving) {
+ CurveEditorStyle style = view->editorStyle();
+
+ QRectF canvas = view->canvasRect().adjusted(0.0, -style.timeAxisHeight, 0.0, 0.0);
+
+ if (canvas.contains(pos))
+ view->setCurrentFrame(std::round(view->mapXtoTime(pos.x())));
+ else if (!m_timer.isActive())
+ m_timer.start();
+ }
+
+ return m_moving;
+}
+
+void Playhead::mouseMoveOutOfBounds(GraphicsView *view)
+{
+ if (QApplication::mouseButtons() != Qt::LeftButton)
+ return;
+
+ CurveEditorStyle style = view->editorStyle();
+ QRectF canvas = view->canvasRect();
+ QPointF pos = view->globalToScene(QCursor::pos());
+
+ if (pos.x() > canvas.right()) {
+ double speed = (pos.x() - canvas.right());
+ double nextCenter = m_rect.center().x() + speed;
+ double frame = std::round(view->mapXtoTime(nextCenter));
+ view->setCurrentFrame(frame);
+
+ double framePosition = view->mapTimeToX(frame);
+ double rightSideOut = framePosition + style.playhead.width / 2.0 + style.canvasMargin;
+ double overshoot = rightSideOut - canvas.right();
+ view->scrollContent(overshoot, 0.0);
+
+ } else if (pos.x() < canvas.left()) {
+ double speed = (canvas.left() - pos.x());
+ double nextCenter = m_rect.center().x() - speed;
+ double frame = std::round(view->mapXtoTime(nextCenter));
+ view->setCurrentFrame(frame);
+
+ double framePosition = view->mapTimeToX(frame);
+ double leftSideOut = framePosition - style.playhead.width / 2.0 - style.canvasMargin;
+ double undershoot = canvas.left() - leftSideOut;
+ view->scrollContent(-undershoot, 0.0);
+ }
+
+ m_timer.start();
+}
+
+void Playhead::mouseRelease(GraphicsView *view)
+{
+ if (m_moving)
+ emit view->model()->currentFrameChanged(m_frame);
+
+ m_moving = false;
+}
+
+void Playhead::paint(QPainter *painter, GraphicsView *view) const
+{
+ CurveEditorStyle style = view->editorStyle();
+
+ painter->save();
+ painter->setPen(style.playhead.color);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ QRectF rect = m_rect;
+ rect.setBottom(m_rect.top() + m_rect.width());
+
+ QPointF top(rect.center().x(), rect.top());
+ QPointF right(rect.right(), rect.top());
+ QPointF bottom(rect.center().x(), rect.bottom());
+ QPointF left(rect.left(), rect.top());
+
+ QLineF rightToBottom(right, bottom);
+ rightToBottom.setLength(style.playhead.radius);
+
+ QLineF leftToBottom(left, bottom);
+ leftToBottom.setLength(style.playhead.radius);
+
+ QPainterPath path(top);
+ path.lineTo(right - QPointF(style.playhead.radius, 0.));
+ path.quadTo(right, rightToBottom.p2());
+ path.lineTo(bottom);
+ path.lineTo(leftToBottom.p2());
+ path.quadTo(left, left + QPointF(style.playhead.radius, 0.));
+ path.closeSubpath();
+
+ painter->fillPath(path, style.playhead.color);
+ painter->drawLine(top + QPointF(0., 5.), QPointF(m_rect.center().x(), m_rect.bottom()));
+
+ painter->restore();
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/playhead.h b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.h
new file mode 100644
index 0000000000..28d2a21a5c
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/playhead.h
@@ -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 <QRectF>
+#include <QTimer>
+
+QT_BEGIN_NAMESPACE
+class QPainter;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+class GraphicsView;
+
+class Playhead
+{
+public:
+ Playhead(GraphicsView *view);
+
+ int currentFrame() const;
+
+ void paint(QPainter *painter, GraphicsView *view) const;
+
+ void moveToFrame(int frame, GraphicsView *view);
+
+ void resize(GraphicsView *view);
+
+ bool mousePress(const QPointF &pos);
+
+ bool mouseMove(const QPointF &pos, GraphicsView *view);
+
+ void mouseRelease(GraphicsView *view);
+
+private:
+ void mouseMoveOutOfBounds(GraphicsView *view);
+
+ int m_frame;
+
+ bool m_moving;
+
+ QRectF m_rect;
+
+ QTimer m_timer;
+
+ GraphicsView *m_view;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.cpp
new file mode 100644
index 0000000000..fb61682090
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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 "selectableitem.h"
+#include "keyframeitem.h"
+
+#include <QDebug>
+
+namespace DesignTools {
+
+SelectableItem::SelectableItem(QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_active(false)
+ , m_selected(false)
+ , m_preSelected(SelectionMode::Undefined)
+{
+ setFlag(QGraphicsItem::ItemIsSelectable, false);
+
+ setFlag(QGraphicsItem::ItemIsMovable, true);
+ setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+}
+
+SelectableItem::~SelectableItem() {}
+
+bool SelectableItem::activated() const
+{
+ return m_active;
+}
+
+bool SelectableItem::selected() const
+{
+ switch (m_preSelected) {
+ case SelectionMode::Clear:
+ return false;
+ case SelectionMode::New:
+ return true;
+ case SelectionMode::Add:
+ return true;
+ case SelectionMode::Remove:
+ return false;
+ case SelectionMode::Toggle:
+ return !m_selected;
+ default:
+ return m_selected;
+ }
+
+ return false;
+}
+
+void SelectableItem::setActivated(bool active)
+{
+ m_active = active;
+}
+
+void SelectableItem::setPreselected(SelectionMode mode)
+{
+ m_preSelected = mode;
+ selectionCallback();
+}
+
+void SelectableItem::applyPreselection()
+{
+ m_selected = selected();
+ m_preSelected = SelectionMode::Undefined;
+}
+
+void SelectableItem::selectionCallback() {}
+
+void SelectableItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ m_active = true;
+ QGraphicsObject::mousePressEvent(event);
+}
+
+void SelectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (type() == KeyframeItem::Type && !selected())
+ return;
+
+ QGraphicsObject::mouseMoveEvent(event);
+}
+
+void SelectableItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ m_active = false;
+ QGraphicsObject::mouseReleaseEvent(event);
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.h b/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.h
new file mode 100644
index 0000000000..0a2a4898a1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/selectableitem.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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 <QGraphicsObject>
+
+namespace DesignTools {
+
+enum ItemType
+{
+ ItemTypeKeyframe = QGraphicsItem::UserType + 1,
+ ItemTypeHandle = QGraphicsItem::UserType + 2,
+ ItemTypeCurve = QGraphicsItem::UserType + 3
+};
+
+enum class SelectionMode : unsigned int
+{
+ Undefined,
+ Clear,
+ New,
+ Add,
+ Remove,
+ Toggle
+};
+
+class SelectableItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ SelectableItem(QGraphicsItem *parent = nullptr);
+
+ ~SelectableItem() override;
+
+ bool activated() const;
+
+ bool selected() const;
+
+ void setActivated(bool active);
+
+ void setPreselected(SelectionMode mode);
+
+ void applyPreselection();
+
+protected:
+ virtual void selectionCallback();
+
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+
+private:
+ bool m_active;
+
+ bool m_selected;
+
+ SelectionMode m_preSelected;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/selector.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/selector.cpp
new file mode 100644
index 0000000000..633c1ddd39
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/selector.cpp
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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 "selector.h"
+#include "graphicsview.h"
+#include "keyframeitem.h"
+#include "playhead.h"
+
+#include <QApplication>
+
+#include <cmath>
+
+namespace DesignTools {
+
+Selector::Selector() {}
+
+void Selector::paint(QPainter *painter)
+{
+ QPen pen(Qt::white);
+
+ painter->save();
+ painter->setPen(pen);
+
+ if (!m_lasso.isEmpty())
+ painter->drawPath(m_lasso);
+
+ if (!m_rect.isNull())
+ painter->drawRect(m_rect);
+
+ painter->restore();
+}
+
+void Selector::mousePress(QMouseEvent *event, GraphicsView *view)
+{
+ m_shortcut = Shortcut(event);
+
+ if (view->hasActiveHandle())
+ return;
+
+ if (select(SelectionTool::Undefined, view->globalToScene(event->globalPos()), view))
+ applyPreSelection(view);
+
+ m_mouseInit = event->globalPos();
+ m_mouseCurr = event->globalPos();
+
+ QPointF click = view->globalToScene(m_mouseInit);
+
+ m_lasso = QPainterPath(click);
+ m_lasso.closeSubpath();
+
+ m_rect = QRectF(click, click);
+}
+
+void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead)
+{
+ if (m_mouseInit.isNull())
+ return;
+
+ QPointF delta = event->globalPos() - m_mouseInit;
+ if (delta.manhattanLength() < QApplication::startDragDistance())
+ return;
+
+ if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
+ || m_shortcut == m_shortcuts.removeFromSelection
+ || m_shortcut == m_shortcuts.toggleSelection) {
+ if (view->hasActiveItem())
+ return;
+
+ select(m_tool, view->globalToScene(event->globalPos()), view);
+
+ event->accept();
+ view->viewport()->update();
+
+ } else if (m_shortcut == m_shortcuts.zoom) {
+ double bigger = std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y();
+ double factor = bigger / view->width();
+ view->setZoomX(view->zoomX() + factor, m_mouseInit);
+ m_mouseCurr = event->globalPos();
+ event->accept();
+
+ } else if (m_shortcut == m_shortcuts.pan) {
+ view->scrollContent(-delta.x(), -delta.y());
+ playhead.resize(view);
+ m_mouseCurr = event->globalPos();
+ }
+}
+
+void Selector::mouseRelease(QMouseEvent *event, GraphicsView *view)
+{
+ Q_UNUSED(event);
+
+ applyPreSelection(view);
+
+ m_shortcut = Shortcut();
+ m_mouseInit = QPoint();
+ m_mouseCurr = QPoint();
+ m_lasso = QPainterPath();
+ m_rect = QRectF();
+}
+
+bool Selector::select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view)
+{
+ auto selectWidthTool = [this, tool](SelectionMode mode, const QPointF &pos, GraphicsView *view) {
+ switch (tool) {
+ case SelectionTool::Lasso:
+ return lassoSelection(mode, pos, view);
+ case SelectionTool::Rectangle:
+ return rectangleSelection(mode, pos, view);
+ default:
+ return pressSelection(mode, pos, view);
+ }
+ };
+
+ if (m_shortcut == m_shortcuts.newSelection) {
+ clearSelection(view);
+ return selectWidthTool(SelectionMode::New, pos, view);
+ } else if (m_shortcut == m_shortcuts.addToSelection) {
+ return selectWidthTool(SelectionMode::Add, pos, view);
+ } else if (m_shortcut == m_shortcuts.removeFromSelection) {
+ return selectWidthTool(SelectionMode::Remove, pos, view);
+ } else if (m_shortcut == m_shortcuts.toggleSelection) {
+ return selectWidthTool(SelectionMode::Toggle, pos, view);
+ }
+
+ return false;
+}
+
+bool Selector::pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *frame = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ QRectF itemRect = frame->mapRectToScene(frame->boundingRect());
+ if (itemRect.contains(pos)) {
+ frame->setPreselected(mode);
+ out = true;
+ }
+ }
+ }
+ return out;
+}
+
+bool Selector::rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ m_rect.setBottomRight(pos);
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (m_rect.contains(keyframeItem->pos())) {
+ keyframeItem->setPreselected(mode);
+ out = true;
+ } else {
+ keyframeItem->setPreselected(SelectionMode::Undefined);
+ }
+ }
+ }
+ return out;
+}
+
+bool Selector::lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ m_lasso.lineTo(pos);
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (m_lasso.contains(keyframeItem->pos())) {
+ keyframeItem->setPreselected(mode);
+ out = true;
+ } else {
+ keyframeItem->setPreselected(SelectionMode::Undefined);
+ }
+ }
+ }
+ return out;
+}
+
+void Selector::clearSelection(GraphicsView *view)
+{
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ frameItem->setPreselected(SelectionMode::Clear);
+ frameItem->applyPreselection();
+ }
+ }
+}
+
+void Selector::applyPreSelection(GraphicsView *view)
+{
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item))
+ keyframeItem->applyPreselection();
+ }
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/selector.h b/src/plugins/qmldesigner/components/curveeditor/detail/selector.h
new file mode 100644
index 0000000000..e8f53d9d59
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/selector.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+#include "selectableitem.h"
+
+#include <QMouseEvent>
+#include <QPainterPath>
+#include <QPoint>
+#include <QRectF>
+
+namespace DesignTools {
+
+class GraphicsView;
+class Playhead;
+
+enum class SelectionTool { Undefined, Lasso, Rectangle };
+
+class Selector
+{
+public:
+ Selector();
+
+ void paint(QPainter *painter);
+
+ void mousePress(QMouseEvent *event, GraphicsView *view);
+
+ void mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead);
+
+ void mouseRelease(QMouseEvent *event, GraphicsView *view);
+
+private:
+ bool select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view);
+
+ bool pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ bool rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ bool lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ void clearSelection(GraphicsView *view);
+
+ void applyPreSelection(GraphicsView *view);
+
+ Shortcuts m_shortcuts = Shortcuts();
+
+ Shortcut m_shortcut;
+
+ SelectionMode m_mode = SelectionMode::Undefined;
+
+ SelectionTool m_tool = SelectionTool::Rectangle;
+
+ QPoint m_mouseInit = QPoint();
+
+ QPoint m_mouseCurr = QPoint();
+
+ QPainterPath m_lasso = QPainterPath();
+
+ QRectF m_rect = QRectF();
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.cpp
new file mode 100644
index 0000000000..d6d04dbbae
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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 "shortcut.h"
+
+namespace DesignTools {
+
+Shortcut::Shortcut()
+ : m_key()
+ , m_buttons()
+ , m_modifiers()
+{}
+
+Shortcut::Shortcut(QMouseEvent *event)
+ : m_key()
+ , m_buttons(event->buttons())
+ , m_modifiers(event->modifiers())
+{}
+
+Shortcut::Shortcut(const Qt::KeyboardModifiers &mods, const Qt::Key &key)
+ : m_key(key)
+ , m_buttons()
+ , m_modifiers(mods)
+{}
+
+Shortcut::Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods)
+ : m_key()
+ , m_buttons(buttons)
+ , m_modifiers(mods)
+{}
+
+Shortcut::Shortcut(const Qt::MouseButtons &buttons,
+ const Qt::KeyboardModifiers &mods,
+ const Qt::Key &key)
+ : m_key(key)
+ , m_buttons(buttons)
+ , m_modifiers(mods)
+{}
+
+bool Shortcut::exactMatch(const Qt::Key &key) const
+{
+ return m_key == key;
+}
+
+bool Shortcut::exactMatch(const Qt::MouseButton &button) const
+{
+ return static_cast<int>(m_buttons) == static_cast<int>(button);
+}
+
+bool Shortcut::exactMatch(const Qt::KeyboardModifier &modifier) const
+{
+ return static_cast<int>(m_modifiers) == static_cast<int>(modifier);
+}
+
+bool Shortcut::operator==(const Shortcut &other) const
+{
+ return m_key == other.m_key && m_buttons == other.m_buttons && m_modifiers == other.m_modifiers;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.h b/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.h
new file mode 100644
index 0000000000..a9e075bd8b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/shortcut.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 <QMouseEvent>
+
+namespace DesignTools {
+
+class Shortcut
+{
+public:
+ Shortcut();
+
+ Shortcut(QMouseEvent *event);
+
+ Shortcut(const Qt::KeyboardModifiers &mods, const Qt::Key &key);
+
+ Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods = Qt::NoModifier);
+
+ Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods, const Qt::Key &key);
+
+ bool exactMatch(const Qt::Key &key) const;
+
+ bool exactMatch(const Qt::MouseButton &button) const;
+
+ bool exactMatch(const Qt::KeyboardModifier &modifier) const;
+
+ bool operator==(const Shortcut &other) const;
+
+private:
+ Qt::Key m_key;
+
+ Qt::MouseButtons m_buttons;
+
+ Qt::KeyboardModifiers m_modifiers;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.cpp
new file mode 100644
index 0000000000..21633ddce1
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** 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 "treeitemdelegate.h"
+#include "treeitem.h"
+
+#include <QEvent>
+#include <QMouseEvent>
+#include <QPainter>
+
+namespace DesignTools {
+
+TreeItemDelegate::TreeItemDelegate(const CurveEditorStyle &style, QObject *parent)
+ : QStyledItemDelegate(parent)
+ , m_style(style)
+{}
+
+QSize TreeItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ return QStyledItemDelegate::sizeHint(option, index);
+}
+
+void TreeItemDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ if (index.column() == 1 || index.column() == 2) {
+
+ int height = option.rect.size().height();
+ QRect iconRect(QPoint(0, 0), QSize(height, height));
+ iconRect.moveCenter(option.rect.center());
+
+ auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
+ if (option.state & QStyle::State_MouseOver && iconRect.contains(m_mousePos)) {
+
+ painter->fillRect(option.rect, option.backgroundBrush);
+
+ if (index.column() == 1) {
+
+ if (treeItem->locked()) {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.unlockedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+
+ } else {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.lockedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+ }
+
+ } else if (index.column() == 2) {
+
+ if (treeItem->pinned()) {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.unpinnedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+
+ } else {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.pinnedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+
+ }
+ }
+
+ } else {
+
+ if (treeItem->locked() && index.column() == 1) {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.lockedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+
+ } else if (treeItem->pinned() && index.column() == 2) {
+
+ QPixmap pixmap = pixmapFromIcon(
+ m_style.treeItemStyle.pinnedIcon,
+ iconRect.size(),
+ m_style.fontColor);
+
+ painter->drawPixmap(iconRect, pixmap);
+
+ }
+ }
+ } else {
+ QStyledItemDelegate::paint(painter, option, index);
+ }
+}
+
+void TreeItemDelegate::setStyle(const CurveEditorStyle &style)
+{
+ m_style = style;
+}
+
+bool TreeItemDelegate::editorEvent(QEvent *event,
+ QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index)
+{
+ if (event->type() == QEvent::MouseMove)
+ m_mousePos = static_cast<QMouseEvent *>(event)->pos();
+
+ return QStyledItemDelegate::editorEvent(event, model, option, index);
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.h b/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.h
new file mode 100644
index 0000000000..6479f48942
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treeitemdelegate.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 "curveeditorstyle.h"
+
+#include <QStyledItemDelegate>
+
+namespace DesignTools {
+
+class TreeItemDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ TreeItemDelegate(const CurveEditorStyle &style, QObject *parent = nullptr);
+
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+
+ void paint(
+ QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+ void setStyle(const CurveEditorStyle &style);
+
+protected:
+ bool editorEvent(
+ QEvent *event,
+ QAbstractItemModel *model,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) override;
+
+private:
+ CurveEditorStyle m_style;
+
+ QPoint m_mousePos;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.cpp
new file mode 100644
index 0000000000..1ea037091d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** 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 "treemodel.h"
+#include "treeitem.h"
+#include "detail/graphicsview.h"
+
+#include <QIcon>
+
+namespace DesignTools {
+
+TreeModel::TreeModel(QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_view(nullptr)
+ , m_root(new TreeItem("Root"))
+{}
+
+TreeModel::~TreeModel()
+{
+ if (m_root) {
+ delete m_root;
+ m_root = nullptr;
+ }
+
+ m_view = nullptr;
+}
+
+QVariant TreeModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
+
+ if (role == Qt::DecorationRole && index.column() == 0)
+ return item->icon();
+
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ return item->data(index.column());
+}
+
+QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if (orientation == Qt::Horizontal)
+ return m_root->headerData(section);
+
+ return QVariant();
+}
+
+QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ TreeItem *parentItem = m_root;
+
+ if (parent.isValid())
+ parentItem = static_cast<TreeItem *>(parent.internalPointer());
+
+ if (TreeItem *childItem = parentItem->child(row))
+ return createIndex(row, column, childItem);
+
+ return QModelIndex();
+}
+
+QModelIndex TreeModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ TreeItem *childItem = static_cast<TreeItem *>(index.internalPointer());
+
+ if (TreeItem *parentItem = childItem->parent()) {
+ if (parentItem == m_root)
+ return QModelIndex();
+
+ return createIndex(parentItem->row(), 0, parentItem);
+ }
+ return QModelIndex();
+}
+
+int TreeModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ TreeItem *parentItem = m_root;
+
+ if (parent.isValid())
+ parentItem = static_cast<TreeItem *>(parent.internalPointer());
+
+ return parentItem->rowCount();
+}
+
+int TreeModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return m_root->columnCount();
+}
+
+void TreeModel::setGraphicsView(GraphicsView *view)
+{
+ m_view = view;
+}
+
+GraphicsView *TreeModel::graphicsView() const
+{
+ return m_view;
+}
+
+void TreeModel::initialize()
+{
+ if (m_root)
+ delete m_root;
+
+ m_root = new TreeItem("Root");
+}
+
+TreeItem *TreeModel::root()
+{
+ return m_root;
+}
+
+TreeItem *TreeModel::find(unsigned int id)
+{
+ return m_root->find(id);
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.h b/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.h
new file mode 100644
index 0000000000..209b2ee506
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treemodel.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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 <QAbstractItemModel>
+
+#include <vector>
+
+namespace DesignTools {
+
+class GraphicsView;
+class TreeItem;
+
+class TreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ TreeModel(QObject *parent = nullptr);
+
+ ~TreeModel() override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+
+ QModelIndex parent(const QModelIndex &index) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ void setGraphicsView(GraphicsView *view);
+
+protected:
+ GraphicsView *graphicsView() const;
+
+ void initialize();
+
+ TreeItem *root();
+
+ TreeItem *find(unsigned int id);
+
+private:
+ GraphicsView *m_view;
+
+ TreeItem *m_root;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treeview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/treeview.cpp
new file mode 100644
index 0000000000..3b9a4ef7c5
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treeview.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** 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 "treeview.h"
+#include "curveeditormodel.h"
+#include "curveitem.h"
+#include "treeitem.h"
+#include "treeitemdelegate.h"
+
+#include <QHeaderView>
+#include <QMouseEvent>
+
+namespace DesignTools {
+
+TreeView::TreeView(CurveEditorModel *model, QWidget *parent)
+ : QTreeView(parent)
+{
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+ setUniformRowHeights(true);
+ setRootIsDecorated(false);
+ setMouseTracking(true);
+ setHeaderHidden(true);
+
+ model->setParent(this);
+ setModel(model);
+
+ auto expandItems = [this]() { expandAll(); };
+ connect(model, &QAbstractItemModel::modelReset, expandItems);
+
+ auto *delegate = new TreeItemDelegate(model->style(), this);
+ setItemDelegate(delegate);
+
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &TreeView::changeSelection);
+ setStyle(model->style());
+
+ header()->setSectionResizeMode(0, QHeaderView::Stretch);
+ header()->setSectionResizeMode(1, QHeaderView::Fixed);
+ header()->setSectionResizeMode(2, QHeaderView::Fixed);
+
+ header()->setStretchLastSection(false);
+ header()->resizeSection(1, 20);
+ header()->resizeSection(2, 20);
+}
+
+void TreeView::setStyle(const CurveEditorStyle &style)
+{
+ QPalette pal = palette();
+ pal.setBrush(QPalette::Base, style.backgroundBrush);
+ pal.setBrush(QPalette::Button, style.backgroundAlternateBrush);
+ pal.setBrush(QPalette::Text, style.fontColor);
+
+ // Tmp to see what happens on windows/macOS.
+ pal.setBrush(backgroundRole(), Qt::white);
+ pal.setBrush(foregroundRole(), Qt::white);
+
+ setPalette(pal);
+
+ if (auto *delegate = qobject_cast<TreeItemDelegate *>(itemDelegate()))
+ delegate->setStyle(style);
+}
+
+void TreeView::changeCurve(unsigned int id, const AnimationCurve &curve)
+{
+ if (auto *curveModel = qobject_cast<CurveEditorModel *>(model()))
+ curveModel->setCurve(id, curve);
+}
+
+void TreeView::changeSelection(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(selected);
+ Q_UNUSED(deselected);
+
+ std::vector<CurveItem *> curves;
+ for (auto index : selectedIndexes()) {
+ if (index.isValid() && index.column() == 0) {
+ auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
+ if (auto *propertyItem = treeItem->asPropertyItem())
+ curves.push_back(new CurveItem(treeItem->id(), propertyItem->curve()));
+ }
+ }
+
+ emit curvesSelected(curves);
+}
+
+QSize TreeView::sizeHint() const
+{
+ return QSize(170, 300);
+}
+
+void TreeView::mousePressEvent(QMouseEvent *event)
+{
+ QModelIndex index = indexAt(event->pos());
+ if (index.isValid()) {
+ auto *treeItem = static_cast<TreeItem *>(index.internalPointer());
+ if (index.column() == 1)
+ treeItem->setLocked(!treeItem->locked());
+ else if (index.column() == 2)
+ treeItem->setPinned(!treeItem->pinned());
+ }
+ QTreeView::mousePressEvent(event);
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/treeview.h b/src/plugins/qmldesigner/components/curveeditor/detail/treeview.h
new file mode 100644
index 0000000000..9d3af647ad
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/treeview.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 <QTreeView>
+
+namespace DesignTools {
+
+class AnimationCurve;
+class CurveEditorModel;
+class CurveItem;
+
+struct CurveEditorStyle;
+
+class TreeView : public QTreeView
+{
+ Q_OBJECT
+
+signals:
+ void curvesSelected(const std::vector<CurveItem *> &curves);
+
+public:
+ TreeView(CurveEditorModel *model, QWidget *parent = nullptr);
+
+ void changeCurve(unsigned int id, const AnimationCurve &curve);
+
+ void setStyle(const CurveEditorStyle &style);
+
+protected:
+ QSize sizeHint() const override;
+
+ void mousePressEvent(QMouseEvent *event) override;
+
+private:
+ void changeSelection(const QItemSelection &selected, const QItemSelection &deselected);
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp
new file mode 100644
index 0000000000..4933bcbe88
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/utils.cpp
@@ -0,0 +1,107 @@
+
+/****************************************************************************
+**
+** 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 <QPalette>
+#include <QPointF>
+#include <QRectF>
+#include <QTransform>
+
+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)
+{
+ return transform.m11();
+}
+
+double scaleY(const QTransform &transform)
+{
+ return transform.m22();
+}
+
+void grow(QRectF &rect, const QPointF &point)
+{
+ if (rect.left() > point.x())
+ rect.setLeft(point.x());
+
+ if (rect.right() < point.x())
+ rect.setRight(point.x());
+
+ if (rect.top() > point.y())
+ rect.setTop(point.y());
+
+ if (rect.bottom() < point.y())
+ rect.setBottom(point.y());
+}
+
+QRectF bbox(const QRectF &rect, const QTransform &transform)
+{
+ QRectF out = rect;
+ grow(out, transform.map(rect.topLeft()));
+ grow(out, transform.map(rect.topRight()));
+ grow(out, transform.map(rect.bottomLeft()));
+ grow(out, transform.map(rect.bottomRight()));
+ return out;
+}
+
+QPalette singleColorPalette(const QColor &color)
+{
+ QPalette palette;
+ palette.setColor(QPalette::Window, color);
+ palette.setColor(QPalette::Background, color);
+ palette.setColor(QPalette::WindowText, color);
+ palette.setColor(QPalette::Foreground, color);
+ palette.setColor(QPalette::Base, color);
+ palette.setColor(QPalette::AlternateBase, color);
+ palette.setColor(QPalette::ToolTipBase, color);
+ palette.setColor(QPalette::ToolTipText, color);
+ palette.setColor(QPalette::Text, color);
+
+ palette.setColor(QPalette::Button, color);
+ palette.setColor(QPalette::ButtonText, color);
+ palette.setColor(QPalette::BrightText, color);
+ palette.setColor(QPalette::Light, color);
+ palette.setColor(QPalette::Midlight, color);
+ palette.setColor(QPalette::Dark, color);
+ palette.setColor(QPalette::Mid, color);
+ palette.setColor(QPalette::Shadow, color);
+ palette.setColor(QPalette::Highlight, color);
+ palette.setColor(QPalette::HighlightedText, color);
+ palette.setColor(QPalette::Link, color);
+ palette.setColor(QPalette::LinkVisited, color);
+ return palette;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/utils.h b/src/plugins/qmldesigner/components/curveeditor/detail/utils.h
new file mode 100644
index 0000000000..77cbd2c7bf
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/utils.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** 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
+
+QT_BEGIN_NAMESPACE
+class QColor;
+class QPalette;
+class QPointF;
+class QRectF;
+class QTransform;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+double clamp(double val, double lo, double hi);
+
+double lerp(double blend, double a, double b);
+
+double scaleX(const QTransform &transform);
+
+double scaleY(const QTransform &transform);
+
+void grow(QRectF &rect, const QPointF &point);
+
+QRectF bbox(const QRectF &rect, const QTransform &transform);
+
+QPalette singleColorPalette(const QColor &color);
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/keyframe.cpp b/src/plugins/qmldesigner/components/curveeditor/keyframe.cpp
new file mode 100644
index 0000000000..8ff577c0a5
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/keyframe.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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 "keyframe.h"
+
+namespace DesignTools {
+
+Keyframe::Keyframe()
+ : m_position()
+ , m_leftHandle()
+ , m_rightHandle()
+{}
+
+Keyframe::Keyframe(const QPointF &position)
+ : m_position(position)
+ , m_leftHandle()
+ , m_rightHandle()
+{}
+
+Keyframe::Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle)
+ : m_position(position)
+ , m_leftHandle(leftHandle)
+ , m_rightHandle(rightHandle)
+{}
+
+bool Keyframe::hasLeftHandle() const
+{
+ return !m_leftHandle.isNull();
+}
+
+bool Keyframe::hasRightHandle() const
+{
+ return !m_rightHandle.isNull();
+}
+
+QPointF Keyframe::position() const
+{
+ return m_position;
+}
+
+QPointF Keyframe::leftHandle() const
+{
+ return m_leftHandle;
+}
+
+QPointF Keyframe::rightHandle() const
+{
+ return m_rightHandle;
+}
+
+void Keyframe::setPosition(const QPointF &pos)
+{
+ m_position = pos;
+}
+
+void Keyframe::setLeftHandle(const QPointF &pos)
+{
+ m_leftHandle = pos;
+}
+
+void Keyframe::setRightHandle(const QPointF &pos)
+{
+ m_rightHandle = pos;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/keyframe.h b/src/plugins/qmldesigner/components/curveeditor/keyframe.h
new file mode 100644
index 0000000000..5e6042531b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/keyframe.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 <QPointF>
+
+namespace DesignTools {
+
+class Keyframe
+{
+public:
+ Keyframe();
+
+ Keyframe(const QPointF &position);
+
+ Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle);
+
+ bool hasLeftHandle() const;
+
+ bool hasRightHandle() const;
+
+ QPointF position() const;
+
+ QPointF leftHandle() const;
+
+ QPointF rightHandle() const;
+
+ void setPosition(const QPointF &pos);
+
+ void setLeftHandle(const QPointF &pos);
+
+ void setRightHandle(const QPointF &pos);
+
+private:
+ QPointF m_position;
+
+ QPointF m_leftHandle;
+
+ QPointF m_rightHandle;
+};
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/treeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/treeitem.cpp
new file mode 100644
index 0000000000..beed419e54
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/treeitem.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** 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 "treeitem.h"
+
+#include <QIcon>
+#include <QVariant>
+
+namespace DesignTools {
+
+TreeItem::TreeItem(const QString &name)
+ : m_name(name)
+ , m_id(0)
+ , m_locked(false)
+ , m_pinned(false)
+ , m_parent(nullptr)
+ , m_children()
+{}
+
+TreeItem::~TreeItem()
+{
+ m_parent = nullptr;
+
+ qDeleteAll(m_children);
+}
+
+QIcon TreeItem::icon() const
+{
+ return QIcon();
+}
+
+NodeTreeItem *TreeItem::asNodeItem()
+{
+ return nullptr;
+}
+
+PropertyTreeItem *TreeItem::asPropertyItem()
+{
+ return nullptr;
+}
+
+unsigned int TreeItem::id() const
+{
+ return m_id;
+}
+
+bool TreeItem::locked() const
+{
+ return m_locked;
+}
+
+bool TreeItem::pinned() const
+{
+ return m_pinned;
+}
+
+int TreeItem::row() const
+{
+ if (m_parent) {
+ for (size_t i = 0; i < m_parent->m_children.size(); ++i) {
+ if (m_parent->m_children[i] == this)
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+int TreeItem::column() const
+{
+ return 0;
+}
+
+int TreeItem::rowCount() const
+{
+ return m_children.size();
+}
+
+int TreeItem::columnCount() const
+{
+ return 3;
+}
+
+TreeItem *TreeItem::parent() const
+{
+ return m_parent;
+}
+
+TreeItem *TreeItem::child(int row) const
+{
+ if (row < 0 || row >= static_cast<int>(m_children.size()))
+ return nullptr;
+
+ return m_children.at(row);
+}
+
+TreeItem *TreeItem::find(unsigned int id) const
+{
+ for (auto *child : m_children) {
+ if (child->id() == id)
+ return child;
+
+ if (auto *childsChild = child->find(id))
+ return childsChild;
+ }
+
+ return nullptr;
+}
+
+QVariant TreeItem::data(int column) const
+{
+ switch (column) {
+ case 0:
+ return QVariant(m_name);
+ case 1:
+ return QVariant(m_locked);
+ case 2:
+ return QVariant(m_pinned);
+ case 3:
+ return QVariant(m_id);
+ default:
+ return QVariant();
+ }
+}
+
+QVariant TreeItem::headerData(int column) const
+{
+ switch (column) {
+ case 0:
+ return QString("Name");
+ case 1:
+ return QString("L");
+ case 2:
+ return QString("P");
+ case 3:
+ return QString("Id");
+ default:
+ return QVariant();
+ }
+}
+
+void TreeItem::setId(unsigned int &id)
+{
+ m_id = id;
+
+ for (auto *child : m_children)
+ child->setId(++id);
+}
+
+void TreeItem::addChild(TreeItem *child)
+{
+ child->m_parent = this;
+ m_children.push_back(child);
+}
+
+void TreeItem::setLocked(bool locked)
+{
+ m_locked = locked;
+}
+
+void TreeItem::setPinned(bool pinned)
+{
+ m_pinned = pinned;
+}
+
+
+NodeTreeItem::NodeTreeItem(const QString &name, const QIcon &icon)
+ : TreeItem(name)
+ , m_icon(icon)
+{
+ Q_UNUSED(icon);
+}
+
+NodeTreeItem *NodeTreeItem::asNodeItem()
+{
+ return this;
+}
+
+QIcon NodeTreeItem::icon() const
+{
+ return m_icon;
+}
+
+
+PropertyTreeItem::PropertyTreeItem(const QString &name, const AnimationCurve &curve)
+ : TreeItem(name)
+ , m_curve(curve)
+{}
+
+PropertyTreeItem *PropertyTreeItem::asPropertyItem()
+{
+ return this;
+}
+
+AnimationCurve PropertyTreeItem::curve() const
+{
+ return m_curve;
+}
+
+void PropertyTreeItem::setCurve(const AnimationCurve &curve)
+{
+ m_curve = curve;
+}
+
+} // End namespace DesignTools.
diff --git a/src/plugins/qmldesigner/components/curveeditor/treeitem.h b/src/plugins/qmldesigner/components/curveeditor/treeitem.h
new file mode 100644
index 0000000000..5b31dc2fc8
--- /dev/null
+++ b/src/plugins/qmldesigner/components/curveeditor/treeitem.h
@@ -0,0 +1,137 @@
+
+/****************************************************************************
+**
+** 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 "animationcurve.h"
+
+#include <QIcon>
+#include <QString>
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+class QIcon;
+class QVariant;
+QT_END_NAMESPACE
+
+namespace DesignTools {
+
+class NodeTreeItem;
+class PropertyTreeItem;
+
+class TreeItem
+{
+public:
+ TreeItem(const QString &name);
+
+ virtual ~TreeItem();
+
+ virtual QIcon icon() const;
+
+ virtual NodeTreeItem *asNodeItem();
+
+ virtual PropertyTreeItem *asPropertyItem();
+
+ unsigned int id() const;
+
+ bool locked() const;
+
+ bool pinned() const;
+
+ int row() const;
+
+ int column() const;
+
+ int rowCount() const;
+
+ int columnCount() const;
+
+ TreeItem *parent() const;
+
+ TreeItem *child(int row) const;
+
+ TreeItem *find(unsigned int row) const;
+
+ QVariant data(int column) const;
+
+ QVariant headerData(int column) const;
+
+ void setId(unsigned int &id);
+
+ void addChild(TreeItem *child);
+
+ void setLocked(bool locked);
+
+ void setPinned(bool pinned);
+
+protected:
+ QString m_name;
+
+ unsigned int m_id;
+
+ bool m_locked;
+
+ bool m_pinned;
+
+ TreeItem *m_parent;
+
+ std::vector<TreeItem *> m_children;
+};
+
+
+class NodeTreeItem : public TreeItem
+{
+public:
+ NodeTreeItem(const QString &name, const QIcon &icon);
+
+ NodeTreeItem *asNodeItem() override;
+
+ QIcon icon() const override;
+
+private:
+ QIcon m_icon;
+};
+
+
+class PropertyTreeItem : public TreeItem
+{
+public:
+ PropertyTreeItem(const QString &name, const AnimationCurve &curve);
+
+ PropertyTreeItem *asPropertyItem() override;
+
+ AnimationCurve curve() const;
+
+ void setCurve(const AnimationCurve &curve);
+
+private:
+ using TreeItem::addChild;
+
+ AnimationCurve m_curve;
+};
+
+} // End namespace DesignTools.