aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmleasing/splineeditor.cpp
diff options
context:
space:
mode:
authorAlan Alpert <416365416c@gmail.com>2012-11-19 19:01:50 -0800
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-11-21 16:49:23 +0100
commit69cc8cd7acf36f1d1f73e6e30d7162bfe59c364e (patch)
treeb9e6fb3c4080006933572ec0f7c84979e2b61faa /tools/qmleasing/splineeditor.cpp
parente03e870b7b4aac85e85b56655ccbc5d99c91053f (diff)
Unify qmleasing and easingcurveeditor
Keeping the name qmleasing, but most of the code from easingcurveeditor. easingcurveeditor had more UI and functionality, as qmleasing could only import AfterEffects curves. That functionality has now been added to easingcurveeditor. Change-Id: Iac1a004f13fe33a18449af1b08bd22138d525322 Reviewed-by: Lars Knoll <lars.knoll@digia.com> Reviewed-by: Christopher Adams <chris.adams@jollamobile.com> Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
Diffstat (limited to 'tools/qmleasing/splineeditor.cpp')
-rw-r--r--tools/qmleasing/splineeditor.cpp714
1 files changed, 714 insertions, 0 deletions
diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp
new file mode 100644
index 0000000000..5d1ee8bd31
--- /dev/null
+++ b/tools/qmleasing/splineeditor.cpp
@@ -0,0 +1,714 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "splineeditor.h"
+#include "segmentproperties.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+#include <QContextMenuEvent>
+#include <QDebug>
+#include <QApplication>
+
+const int canvasWidth = 640;
+const int canvasHeight = 320;
+
+const int canvasMargin = 160;
+
+SplineEditor::SplineEditor(QWidget *parent) :
+ QWidget(parent), m_pointListWidget(0), m_block(false)
+{
+ setFixedSize(canvasWidth + canvasMargin * 2, canvasHeight + canvasMargin * 2);
+
+ m_controlPoints.append(QPointF(0.4, 0.075));
+ m_controlPoints.append(QPointF(0.45,0.24));
+ m_controlPoints.append(QPointF(0.5,0.5));
+
+ m_controlPoints.append(QPointF(0.55,0.76));
+ m_controlPoints.append(QPointF(0.7,0.9));
+ m_controlPoints.append(QPointF(1.0, 1.0));
+
+ m_numberOfSegments = 2;
+
+ m_activeControlPoint = -1;
+
+ m_mouseDrag = false;
+
+ m_pointContextMenu = new QMenu(this);
+ m_deleteAction = new QAction(tr("Delete point"), m_pointContextMenu);
+ m_smoothAction = new QAction(tr("Smooth point"), m_pointContextMenu);
+ m_cornerAction = new QAction(tr("Corner point"), m_pointContextMenu);
+
+ m_smoothAction->setCheckable(true);
+
+ m_pointContextMenu->addAction(m_deleteAction);
+ m_pointContextMenu->addAction(m_smoothAction);
+ m_pointContextMenu->addAction(m_cornerAction);
+
+ m_curveContextMenu = new QMenu(this);
+
+ m_addPoint = new QAction(tr("Add point"), m_pointContextMenu);
+
+ m_curveContextMenu->addAction(m_addPoint);
+
+ initPresets();
+
+ invalidateSmoothList();
+}
+
+static inline QPointF mapToCanvas(const QPointF &point)
+{
+ return QPointF(point.x() * canvasWidth + canvasMargin,
+ canvasHeight - point.y() * canvasHeight + canvasMargin);
+}
+
+static inline QPointF mapFromCanvas(const QPointF &point)
+{
+ return QPointF((point.x() - canvasMargin) / canvasWidth ,
+ 1 - (point.y() - canvasMargin) / canvasHeight);
+}
+
+static inline void paintControlPoint(const QPointF &controlPoint, QPainter *painter, bool edit,
+ bool realPoint, bool active, bool smooth)
+{
+ int pointSize = 4;
+
+ if (active)
+ painter->setBrush(QColor(140, 140, 240, 255));
+ else
+ painter->setBrush(QColor(120, 120, 220, 255));
+
+ if (realPoint) {
+ pointSize = 6;
+ painter->setBrush(QColor(80, 80, 210, 150));
+ }
+
+ painter->setPen(QColor(50, 50, 50, 140));
+
+ if (!edit)
+ painter->setBrush(QColor(160, 80, 80, 250));
+
+ if (smooth) {
+ painter->drawEllipse(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
+ mapToCanvas(controlPoint).y() - pointSize + 0.5,
+ pointSize * 2, pointSize * 2));
+ } else {
+ painter->drawRect(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
+ mapToCanvas(controlPoint).y() - pointSize + 0.5,
+ pointSize * 2, pointSize * 2));
+ }
+}
+
+static inline bool indexIsRealPoint(int i)
+{
+ return !((i + 1) % 3);
+}
+
+static inline int pointForControlPoint(int i)
+{
+ if ((i % 3) == 0)
+ return i - 1;
+
+ if ((i % 3) == 1)
+ return i + 1;
+
+ return i;
+}
+
+void drawCleanLine(QPainter *painter, const QPoint p1, QPoint p2)
+{
+ painter->drawLine(p1 + QPointF(0.5 , 0.5), p2 + QPointF(0.5, 0.5));
+}
+
+void SplineEditor::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+
+ QPen pen(Qt::black);
+ pen.setWidth(1);
+ painter.fillRect(0,0,width() - 1, height() - 1, QBrush(Qt::white));
+ painter.drawRect(0,0,width() - 1, height() - 1);
+
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ pen = QPen(Qt::gray);
+ pen.setWidth(1);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ drawCleanLine(&painter,mapToCanvas(QPoint(0, 0)).toPoint(), mapToCanvas(QPoint(1, 0)).toPoint());
+ drawCleanLine(&painter,mapToCanvas(QPoint(0, 1)).toPoint(), mapToCanvas(QPoint(1, 1)).toPoint());
+
+ for (int i = 0; i < m_numberOfSegments; i++) {
+ QPainterPath path;
+ QPointF p0;
+
+ if (i == 0)
+ p0 = mapToCanvas(QPointF(0.0, 0.0));
+ else
+ p0 = mapToCanvas(m_controlPoints.at(i * 3 - 1));
+
+ path.moveTo(p0);
+
+ QPointF p1 = mapToCanvas(m_controlPoints.at(i * 3));
+ QPointF p2 = mapToCanvas(m_controlPoints.at(i * 3 + 1));
+ QPointF p3 = mapToCanvas(m_controlPoints.at(i * 3 + 2));
+ path.cubicTo(p1, p2, p3);
+ painter.strokePath(path, QPen(QBrush(Qt::black), 2));
+
+ QPen pen(Qt::black);
+ pen.setWidth(1);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ painter.drawLine(p0, p1);
+ painter.drawLine(p3, p2);
+ }
+
+ paintControlPoint(QPointF(0.0, 0.0), &painter, false, true, false, false);
+ paintControlPoint(QPointF(1.0, 1.0), &painter, false, true, false, false);
+
+ for (int i = 0; i < m_controlPoints.count() - 1; ++i)
+ paintControlPoint(m_controlPoints.at(i),
+ &painter,
+ true,
+ indexIsRealPoint(i),
+ i == m_activeControlPoint,
+ isControlPointSmooth(i));
+}
+
+void SplineEditor::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == Qt::LeftButton) {
+ m_activeControlPoint = findControlPoint(e->pos());
+
+ if (m_activeControlPoint != -1) {
+ mouseMoveEvent(e);
+ }
+ m_mousePress = e->pos();
+ e->accept();
+ }
+}
+
+void SplineEditor::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == Qt::LeftButton) {
+ m_activeControlPoint = -1;
+
+ m_mouseDrag = false;
+ e->accept();
+ }
+}
+
+void SplineEditor::contextMenuEvent(QContextMenuEvent *e)
+{
+ int index = findControlPoint(e->pos());
+
+ if (index > 0 && indexIsRealPoint(index)) {
+ m_smoothAction->setChecked(isControlPointSmooth(index));
+ QAction* action = m_pointContextMenu->exec(e->globalPos());
+ if (action == m_deleteAction)
+ deletePoint(index);
+ else if (action == m_smoothAction)
+ smoothPoint(index);
+ else if (action == m_cornerAction)
+ cornerPoint(index);
+ } else {
+ QAction* action = m_curveContextMenu->exec(e->globalPos());
+ if (action == m_addPoint)
+ addPoint(e->pos());
+ }
+}
+
+void SplineEditor::invalidate()
+{
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ easingCurve.addCubicBezierSegment(m_controlPoints.at(i * 3),
+ m_controlPoints.at(i * 3 + 1),
+ m_controlPoints.at(i * 3 + 2));
+ }
+ setEasingCurve(easingCurve);
+ invalidateSegmentProperties();
+}
+
+void SplineEditor::invalidateSmoothList()
+{
+ m_smoothList.clear();
+
+ for (int i = 0; i < (m_numberOfSegments - 1); ++i)
+ m_smoothList.append(isSmooth(i * 3 + 2));
+
+}
+
+void SplineEditor::invalidateSegmentProperties()
+{
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ SegmentProperties *segmentProperties = m_segmentProperties.at(i);
+ bool smooth = false;
+ if (i < (m_numberOfSegments - 1)) {
+ smooth = m_smoothList.at(i);
+ }
+ segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
+ }
+}
+
+QHash<QString, QEasingCurve> SplineEditor::presets() const
+{
+ return m_presets;
+}
+
+QString SplineEditor::generateCode()
+{
+ QString s = QLatin1String("[");
+ foreach (const QPointF &point, m_controlPoints) {
+ s += QString::number(point.x(), 'g', 2) + "," + QString::number(point.y(), 'g', 3) + ",";
+ }
+ s.chop(1); //removing last ","
+ s += "]";
+
+ return s;
+}
+
+QStringList SplineEditor::presetNames() const
+{
+ return m_presets.keys();
+}
+
+QWidget *SplineEditor::pointListWidget()
+{
+ if (!m_pointListWidget) {
+ setupPointListWidget();
+ }
+
+ return m_pointListWidget;
+}
+
+int SplineEditor::findControlPoint(const QPoint &point)
+{
+ int pointIndex = -1;
+ qreal distance = -1;
+ for (int i = 0; i<m_controlPoints.size() - 1; ++i) {
+ qreal d = QLineF(point, mapToCanvas(m_controlPoints.at(i))).length();
+ if ((distance < 0 && d < 10) || d < distance) {
+ distance = d;
+ pointIndex = i;
+ }
+ }
+ return pointIndex;
+}
+
+static inline bool veryFuzzyCompare(qreal r1, qreal r2)
+{
+ if (qFuzzyCompare(r1, 2))
+ return true;
+
+ int r1i = qRound(r1 * 20);
+ int r2i = qRound(r2 * 20);
+
+ if (qFuzzyCompare(qreal(r1i) / 20, qreal(r2i) / 20))
+ return true;
+
+ return false;
+}
+
+bool SplineEditor::isSmooth(int i) const
+{
+ if (i == 0)
+ return false;
+
+ QPointF p = m_controlPoints.at(i);
+ QPointF p_before = m_controlPoints.at(i - 1);
+ QPointF p_after = m_controlPoints.at(i + 1);
+
+ QPointF v1 = p_after - p;
+ v1 = v1 / v1.manhattanLength(); //normalize
+
+ QPointF v2 = p - p_before;
+ v2 = v2 / v2.manhattanLength(); //normalize
+
+ return veryFuzzyCompare(v1.x(), v2.x()) && veryFuzzyCompare(v1.y(), v2.y());
+}
+
+void SplineEditor::smoothPoint(int index)
+{
+ if (m_smoothAction->isChecked()) {
+
+ QPointF before = QPointF(0,0);
+ if (index > 3)
+ before = m_controlPoints.at(index - 3);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((index + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(index + 3);
+
+ QPointF tangent = (after - before) / 6;
+
+ QPointF thisPoint = m_controlPoints.at(index);
+
+ if (index > 0)
+ m_controlPoints[index - 1] = thisPoint - tangent;
+
+ if (index + 1 < m_controlPoints.count())
+ m_controlPoints[index + 1] = thisPoint + tangent;
+
+ m_smoothList[index / 3] = true;
+ } else {
+ m_smoothList[index / 3] = false;
+ }
+ invalidate();
+ update();
+}
+
+void SplineEditor::cornerPoint(int index)
+{
+ QPointF before = QPointF(0,0);
+ if (index > 3)
+ before = m_controlPoints.at(index - 3);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((index + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(index + 3);
+
+ QPointF thisPoint = m_controlPoints.at(index);
+
+ if (index > 0)
+ m_controlPoints[index - 1] = (before - thisPoint) / 3 + thisPoint;
+
+ if (index + 1 < m_controlPoints.count())
+ m_controlPoints[index + 1] = (after - thisPoint) / 3 + thisPoint;
+
+ m_smoothList[(index) / 3] = false;
+ invalidate();
+}
+
+void SplineEditor::deletePoint(int index)
+{
+ m_controlPoints.remove(index - 1, 3);
+ m_numberOfSegments--;
+
+ invalidateSmoothList();
+ setupPointListWidget();
+ invalidate();
+}
+
+void SplineEditor::addPoint(const QPointF point)
+{
+ QPointF newPos = mapFromCanvas(point);
+ int splitIndex = 0;
+ for (int i=0; i < m_controlPoints.size() - 1; ++i) {
+ if (indexIsRealPoint(i) && m_controlPoints.at(i).x() > newPos.x()) {
+ break;
+ } else if (indexIsRealPoint(i))
+ splitIndex = i;
+ }
+ QPointF before = QPointF(0,0);
+ if (splitIndex > 0)
+ before = m_controlPoints.at(splitIndex);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((splitIndex + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(splitIndex + 3);
+
+ if (splitIndex > 0) {
+ m_controlPoints.insert(splitIndex + 2, (newPos + after) / 2);
+ m_controlPoints.insert(splitIndex + 2, newPos);
+ m_controlPoints.insert(splitIndex + 2, (newPos + before) / 2);
+ } else {
+ m_controlPoints.insert(splitIndex + 1, (newPos + after) / 2);
+ m_controlPoints.insert(splitIndex + 1, newPos);
+ m_controlPoints.insert(splitIndex + 1, (newPos + before) / 2);
+ }
+ m_numberOfSegments++;
+
+ invalidateSmoothList();
+ setupPointListWidget();
+ invalidate();
+}
+
+void SplineEditor::initPresets()
+{
+ const QPointF endPoint(1.0, 1.0);
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.45, 0.24), QPointF(0.5, 0.5));
+ easingCurve.addCubicBezierSegment(QPointF(0.55, 0.76), QPointF(0.7, 0.9), endPoint);
+ m_presets.insert(tr("Standard Easing"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.65, 1), endPoint);
+ m_presets.insert(tr("Simple"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.38, 0.51), QPointF(0.57, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.8, 0.69), QPointF(0.65, 1), endPoint);
+ m_presets.insert(tr("Simple Bounce"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.64, -0.0025), QPointF(0.74, 0.23));
+ easingCurve.addCubicBezierSegment(QPointF(0.84, 0.46), QPointF(0.91, 0.77), endPoint);
+ m_presets.insert(tr("Slow in fast out"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.47, 0.51), QPointF(0.59, 0.94));
+ easingCurve.addCubicBezierSegment(QPointF(0.84, 0.95), QPointF( 0.99, 0.94), endPoint);
+ m_presets.insert(tr("Snapping"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF( 0.38, 0.35),QPointF(0.38, 0.7), QPointF(0.45, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.48, 0.66), QPointF(0.62, 0.62), QPointF(0.66, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.69, 0.76), QPointF(0.77, 0.76), QPointF(0.79, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.83, 0.91), QPointF(0.87, 0.92), QPointF(0.91, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.95, 0.95), QPointF(0.97, 0.94), endPoint);
+ m_presets.insert(tr("Complex Bounce"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve4(QEasingCurve::BezierSpline);
+ easingCurve4.addCubicBezierSegment(QPointF(0.12, -0.12),QPointF(0.23, -0.19), QPointF( 0.35, -0.09));
+ easingCurve4.addCubicBezierSegment(QPointF(0.47, 0.005), QPointF(0.52, 1), QPointF(0.62, 1.1));
+ easingCurve4.addCubicBezierSegment(QPointF(0.73, 1.2), QPointF(0.91,1 ), endPoint);
+ m_presets.insert(tr("Overshoot"), easingCurve4);
+ }
+}
+
+void SplineEditor::setupPointListWidget()
+{
+ if (!m_pointListWidget)
+ m_pointListWidget = new QScrollArea(this);
+
+ if (m_pointListWidget->widget())
+ delete m_pointListWidget->widget();
+
+ m_pointListWidget->setFrameStyle(QFrame::NoFrame);
+ m_pointListWidget->setWidgetResizable(true);
+ m_pointListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ m_pointListWidget->setWidget(new QWidget(m_pointListWidget));
+ QVBoxLayout *layout = new QVBoxLayout(m_pointListWidget->widget());
+ layout->setMargin(0);
+ layout->setSpacing(2);
+ m_pointListWidget->widget()->setLayout(layout);
+
+ m_segmentProperties.clear();
+
+ { //implicit 0,0
+ QWidget *widget = new QWidget(m_pointListWidget->widget());
+ Ui_Pane pane;
+ pane.setupUi(widget);
+ pane.p1_x->setValue(0);
+ pane.p1_y->setValue(0);
+ layout->addWidget(widget);
+ pane.label->setText("p0");
+ widget->setEnabled(false);
+ }
+
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ SegmentProperties *segmentProperties = new SegmentProperties(m_pointListWidget->widget());
+ layout->addWidget(segmentProperties);
+ bool smooth = false;
+ if (i < (m_numberOfSegments - 1)) {
+ smooth = m_smoothList.at(i);
+ }
+ segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
+ segmentProperties->setSplineEditor(this);
+ m_segmentProperties << segmentProperties;
+ }
+ layout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding));
+
+ m_pointListWidget->viewport()->show();
+ m_pointListWidget->viewport()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ m_pointListWidget->show();
+}
+
+bool SplineEditor::isControlPointSmooth(int i) const
+{
+ if (i == 0)
+ return false;
+
+ if (i == m_controlPoints.count() - 1)
+ return false;
+
+ if (m_numberOfSegments == 1)
+ return false;
+
+ int index = pointForControlPoint(i);
+
+ if (index == 0)
+ return false;
+
+ if (index == m_controlPoints.count() - 1)
+ return false;
+
+ return m_smoothList.at(index / 3);
+}
+
+QPointF limitToCanvas(const QPointF point)
+{
+ qreal left = -qreal( canvasMargin) / qreal(canvasWidth);
+ qreal width = 1.0 - 2.0 * left;
+
+ qreal top = -qreal( canvasMargin) / qreal(canvasHeight);
+ qreal height = 1.0 - 2.0 * top;
+
+ QPointF p = point;
+ QRectF r(left, top, width, height);
+
+ if (p.x() > r.right()) {
+ p.setX(r.right());
+ }
+ if (p.x() < r.left()) {
+ p.setX(r.left());
+ }
+ if (p.y() < r.top()) {
+ p.setY(r.top());
+ }
+ if (p.y() > r.bottom()) {
+ p.setY(r.bottom());
+ }
+ return p;
+}
+
+void SplineEditor::mouseMoveEvent(QMouseEvent *e)
+{
+ // If we've moved more then 25 pixels, assume user is dragging
+ if (!m_mouseDrag && QPoint(m_mousePress - e->pos()).manhattanLength() > qApp->startDragDistance())
+ m_mouseDrag = true;
+
+ QPointF p = mapFromCanvas(e->pos());
+
+ if (m_mouseDrag && m_activeControlPoint >= 0 && m_activeControlPoint < m_controlPoints.size()) {
+ p = limitToCanvas(p);
+ if (indexIsRealPoint(m_activeControlPoint)) {
+ //move also the tangents
+ QPointF targetPoint = p;
+ QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
+ m_controlPoints[m_activeControlPoint] = targetPoint;
+ m_controlPoints[m_activeControlPoint - 1] += distance;
+ m_controlPoints[m_activeControlPoint + 1] += distance;
+ } else {
+ if (!isControlPointSmooth(m_activeControlPoint)) {
+ m_controlPoints[m_activeControlPoint] = p;
+ } else {
+ QPointF targetPoint = p;
+ QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
+ m_controlPoints[m_activeControlPoint] = p;
+
+ if ((m_activeControlPoint > 1) && (m_activeControlPoint % 3) == 0) { //right control point
+ m_controlPoints[m_activeControlPoint - 2] -= distance;
+ } else if ((m_activeControlPoint < (m_controlPoints.count() - 2)) //left control point
+ && (m_activeControlPoint % 3) == 1) {
+ m_controlPoints[m_activeControlPoint + 2] -= distance;
+ }
+ }
+ }
+ invalidate();
+ }
+}
+
+void SplineEditor::setEasingCurve(const QEasingCurve &easingCurve)
+{
+ if (m_easingCurve == easingCurve)
+ return;
+ m_block = true;
+ m_easingCurve = easingCurve;
+ m_controlPoints = m_easingCurve.toCubicSpline();
+ m_numberOfSegments = m_controlPoints.count() / 3;
+ update();
+ emit easingCurveChanged();
+
+ const QString code = generateCode();
+ emit easingCurveCodeChanged(code);
+
+ m_block = false;
+}
+
+void SplineEditor::setPreset(const QString &name)
+{
+ setEasingCurve(m_presets.value(name));
+ invalidateSmoothList();
+ setupPointListWidget();
+}
+
+void SplineEditor::setEasingCurve(const QString &code)
+{
+ if (m_block)
+ return;
+ if (code.left(1) == QLatin1String("[") && code.right(1) == QLatin1String("]")) {
+ QString cleanCode = code;
+ cleanCode.remove(0, 1);
+ cleanCode.chop(1);
+ const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
+ if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
+ QList<qreal> realList;
+ foreach (const QString &string, stringList) {
+ bool ok;
+ realList.append(string.toDouble(&ok));
+ if (!ok)
+ return;
+ }
+ QList<QPointF> points;
+ for (int i = 0; i < realList.count() / 2; ++i)
+ points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
+ if (points.last() == QPointF(1.0, 1.0)) {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+
+ for (int i = 0; i < points.count() / 3; ++i) {
+ easingCurve.addCubicBezierSegment(points.at(i * 3),
+ points.at(i * 3 + 1),
+ points.at(i * 3 + 2));
+ }
+ setEasingCurve(easingCurve);
+ invalidateSmoothList();
+ setupPointListWidget();
+ }
+ }
+ }
+}