Adding a custom easing curve editor to declarative tools
authorThomas Hartmann <Thomas.Hartmann@nokia.com>
Tue, 13 Mar 2012 09:27:59 +0000 (10:27 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 13 Mar 2012 13:00:45 +0000 (14:00 +0100)
Change-Id: Ic8ef77792d74ec99b23d85cd8888e0190acc3e10
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@nokia.com>
Reviewed-by: Alessandro Portale <alessandro.portale@nokia.com>
14 files changed:
tools/easingcurveeditor/Button.qml [new file with mode: 0644]
tools/easingcurveeditor/easingcurveeditor.pro [new file with mode: 0644]
tools/easingcurveeditor/main.cpp [new file with mode: 0644]
tools/easingcurveeditor/mainwindow.cpp [new file with mode: 0644]
tools/easingcurveeditor/mainwindow.h [new file with mode: 0644]
tools/easingcurveeditor/pane.ui [new file with mode: 0644]
tools/easingcurveeditor/preview.qml [new file with mode: 0644]
tools/easingcurveeditor/properties.ui [new file with mode: 0644]
tools/easingcurveeditor/resources.qrc [new file with mode: 0644]
tools/easingcurveeditor/segmentproperties.cpp [new file with mode: 0644]
tools/easingcurveeditor/segmentproperties.h [new file with mode: 0644]
tools/easingcurveeditor/splineeditor.cpp [new file with mode: 0644]
tools/easingcurveeditor/splineeditor.h [new file with mode: 0644]
tools/tools.pro

diff --git a/tools/easingcurveeditor/Button.qml b/tools/easingcurveeditor/Button.qml
new file mode 100644 (file)
index 0000000..a47809a
--- /dev/null
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+    id: button
+
+    signal clicked
+
+    Rectangle {
+        id: normalBackground
+        radius: 4
+        anchors.fill: parent
+        smooth: true
+        gradient: Gradient {
+            GradientStop {
+                position: 0
+                color: "#afafaf"
+            }
+
+            GradientStop {
+                position: 0.460
+                color: "#808080"
+            }
+
+            GradientStop {
+                position: 1
+                color: "#adadad"
+            }
+        }
+        border.color: "#000000"
+    }
+
+
+    Rectangle {
+        id: hoveredBackground
+        x: 2
+        y: -8
+        radius: 4
+        opacity: 0
+        gradient: Gradient {
+            GradientStop {
+                position: 0
+                color: "#cacaca"
+            }
+
+            GradientStop {
+                position: 0.460
+                color: "#a2a2a2"
+            }
+
+            GradientStop {
+                position: 1
+                color: "#c8c8c8"
+            }
+        }
+        smooth: true
+        anchors.fill: parent
+        border.color: "#000000"
+    }
+
+
+    Rectangle {
+        id: pressedBackground
+        x: -8
+        y: 2
+        radius: 4
+        opacity: 0
+        gradient: Gradient {
+            GradientStop {
+                position: 0
+                color: "#8b8b8b"
+            }
+
+            GradientStop {
+                position: 0.470
+                color: "#626161"
+            }
+
+            GradientStop {
+                position: 1
+                color: "#8f8e8e"
+            }
+        }
+        smooth: true
+        anchors.fill: parent
+        border.color: "#000000"
+    }
+    states: [
+        State {
+            name: "hovered"
+
+            PropertyChanges {
+                target: normalBackground
+                opacity: 0
+            }
+
+            PropertyChanges {
+                target: hoveredBackground
+                opacity: 1
+            }
+        },
+        State {
+            name: "pressed"
+
+            PropertyChanges {
+                target: normalBackground
+                opacity: 0
+            }
+
+            PropertyChanges {
+                target: pressedBackground
+                opacity: 1
+            }
+        }
+    ]
+
+    Text {
+        color: "#e8e8e8"
+        text: qsTr("Play")
+        anchors.fill: parent
+        horizontalAlignment: Text.AlignHCenter
+        verticalAlignment: Text.AlignVCenter
+        font.bold: true
+        font.pixelSize: 20
+    }
+
+    MouseArea {
+        hoverEnabled: true
+        anchors.fill: parent
+        onEntered: button.state = "hovered"
+        onExited: button.state = ""
+        onClicked: {
+            button.state = "pressed"
+            button.clicked();
+        }
+    }
+}
diff --git a/tools/easingcurveeditor/easingcurveeditor.pro b/tools/easingcurveeditor/easingcurveeditor.pro
new file mode 100644 (file)
index 0000000..0a266d0
--- /dev/null
@@ -0,0 +1,25 @@
+TEMPLATE = app
+DEPENDPATH += .
+INCLUDEPATH += .
+
+QT += declarative quick widgets
+CONFIG -= app_bundle
+
+# Input
+SOURCES += main.cpp \
+    splineeditor.cpp \
+    mainwindow.cpp \
+    segmentproperties.cpp
+
+RESOURCES = $$PWD/resources.qrc
+
+HEADERS += \
+    splineeditor.h \
+    mainwindow.h \
+    ui_properties.h \
+    ui_pane.h \
+    segmentproperties.h
+
+FORMS += \
+    properties.ui \
+    pane.ui
diff --git a/tools/easingcurveeditor/main.cpp b/tools/easingcurveeditor/main.cpp
new file mode 100644 (file)
index 0000000..05e8ace
--- /dev/null
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <mainwindow.h>
+
+
+int main(int argc, char ** argv)
+{
+    QApplication app(argc, argv);
+
+    MainWindow mainWindow;
+    mainWindow.show();
+    mainWindow.showQuickView();
+
+    return app.exec();
+}
diff --git a/tools/easingcurveeditor/mainwindow.cpp b/tools/easingcurveeditor/mainwindow.cpp
new file mode 100644 (file)
index 0000000..569b74a
--- /dev/null
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "splineeditor.h"
+#include <QtQuick/QQuickView>
+#include <QtQuick>
+#include <QtQml/QQmlContext>
+#include <QEasingCurve>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+
+MainWindow::MainWindow(QWidget *parent) :
+    QMainWindow(parent)
+{
+    SplineEditor *splineEditor = new SplineEditor(this);
+
+    QWidget *mainWidget = new QWidget(this);
+
+    setCentralWidget(mainWidget);
+
+    QHBoxLayout *hboxLayout = new QHBoxLayout(mainWidget);
+    QVBoxLayout *vboxLayout = new QVBoxLayout();
+
+    mainWidget->setLayout(hboxLayout);
+    hboxLayout->addLayout(vboxLayout);
+
+    QWidget *propertyWidget = new QWidget(this);
+    ui_properties.setupUi(propertyWidget);
+
+    ui_properties.spinBox->setMinimum(50);
+    ui_properties.spinBox->setMaximum(10000);
+    ui_properties.spinBox->setValue(500);
+
+    hboxLayout->addWidget(propertyWidget);
+
+    m_placeholder = new QWidget(this);
+
+    m_placeholder->setFixedSize(quickView.size());
+
+    vboxLayout->addWidget(splineEditor);
+    vboxLayout->addWidget(m_placeholder);
+
+    ui_properties.plainTextEdit->setPlainText(splineEditor->generateCode());
+    connect(splineEditor, SIGNAL(easingCurveCodeChanged(QString)), ui_properties.plainTextEdit, SLOT(setPlainText(QString)));
+
+    quickView.rootContext()->setContextProperty(QLatin1String("spinBox"), ui_properties.spinBox);
+
+    foreach (const QString &name, splineEditor->presetNames())
+        ui_properties.comboBox->addItem(name);
+
+    connect(ui_properties.comboBox, SIGNAL(currentIndexChanged(QString)), splineEditor, SLOT(setPreset(QString)));
+
+    splineEditor->setPreset(ui_properties.comboBox->currentText());
+
+    QVBoxLayout *groupBoxLayout = new QVBoxLayout(ui_properties.groupBox);
+    groupBoxLayout->setMargin(0);
+    ui_properties.groupBox->setLayout(groupBoxLayout);
+
+    groupBoxLayout->addWidget(splineEditor->pointListWidget());
+    m_splineEditor = splineEditor;
+    connect(ui_properties.plainTextEdit, SIGNAL(textChanged()), this, SLOT(textEditTextChanged()));
+    initQml();
+}
+
+void MainWindow::showQuickView()
+{
+    const int margin = 16;
+    quickView.move(pos() + QPoint(0, frameGeometry().height() + margin));
+
+    quickView.raise();
+    quickView.show();
+}
+
+void MainWindow::textEditTextChanged()
+{
+    m_splineEditor->setEasingCurve(ui_properties.plainTextEdit->toPlainText().trimmed());
+}
+
+void MainWindow::moveEvent(QMoveEvent *event)
+{
+    QMainWindow::moveEvent(event);
+    showQuickView();
+}
+
+void MainWindow::resizeEvent(QResizeEvent *event)
+{
+    QMainWindow::resizeEvent(event);
+    showQuickView();
+}
+
+void MainWindow::initQml()
+{
+    quickView.setWindowFlags(Qt::FramelessWindowHint);
+    quickView.rootContext()->setContextProperty(QLatin1String("editor"), m_splineEditor);
+    quickView.setSource(QUrl("qrc:/preview.qml"));
+    quickView.show();
+}
diff --git a/tools/easingcurveeditor/mainwindow.h b/tools/easingcurveeditor/mainwindow.h
new file mode 100644 (file)
index 0000000..44591d3
--- /dev/null
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QtQuick/QQuickView>
+#include "ui_properties.h"
+
+class SplineEditor;
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+public:
+    explicit MainWindow(QWidget *parent = 0);
+
+    void showQuickView();
+
+signals:
+
+public slots:
+    void textEditTextChanged();
+
+protected:
+    virtual void moveEvent(QMoveEvent *event);
+    virtual void resizeEvent(QResizeEvent *event);
+    void initQml();
+
+private:
+    QQuickView quickView;
+    QWidget *m_placeholder;
+    Ui_Properties ui_properties;
+    SplineEditor *m_splineEditor;
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/tools/easingcurveeditor/pane.ui b/tools/easingcurveeditor/pane.ui
new file mode 100644 (file)
index 0000000..1500589
--- /dev/null
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Pane</class>
+ <widget class="QWidget" name="Pane">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>416</width>
+    <height>47</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="font">
+      <font>
+       <weight>75</weight>
+       <bold>true</bold>
+      </font>
+     </property>
+     <property name="text">
+      <string>p1</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QWidget" name="widget" native="true">
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <property name="spacing">
+       <number>4</number>
+      </property>
+      <property name="margin">
+       <number>2</number>
+      </property>
+      <item>
+       <widget class="QLabel" name="label_x">
+        <property name="text">
+         <string>x</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QDoubleSpinBox" name="p1_x">
+        <property name="decimals">
+         <number>4</number>
+        </property>
+        <property name="singleStep">
+         <double>0.010000000000000</double>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_y">
+        <property name="text">
+         <string>y</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QDoubleSpinBox" name="p1_y">
+        <property name="decimals">
+         <number>4</number>
+        </property>
+        <property name="minimum">
+         <double>-10.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>10.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.010000000000000</double>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="0" column="2">
+    <widget class="QCheckBox" name="smooth">
+     <property name="text">
+      <string>smooth</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="3" alignment="Qt::AlignVCenter">
+    <spacer name="horizontalSpacer">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>99</width>
+       <height>10</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tools/easingcurveeditor/preview.qml b/tools/easingcurveeditor/preview.qml
new file mode 100644 (file)
index 0000000..61f4862
--- /dev/null
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+    id: root
+    width:  800
+    height: 100
+
+    Rectangle {
+        gradient: Gradient {
+            GradientStop {
+                position: 0
+                color: "#aaa7a7"
+            }
+
+            GradientStop {
+                position: 0.340
+                color: "#a4a4a4"
+            }
+
+            GradientStop {
+                position: 1
+                color: "#6b6b6b"
+            }
+        }
+        anchors.fill: parent
+    }
+
+    Button {
+        id: button
+
+        x: 19
+        y: 20
+        width: 133
+        height: 61
+        onClicked: {
+            if (root.state ==="")
+                root.state = "moved";
+            else
+                root.state = "";
+        }
+    }
+
+    Rectangle {
+        id: groove
+        x: 163
+        y: 20
+        width: 622
+        height: 61
+        color: "#919191"
+        radius: 4
+        border.color: "#adadad"
+
+        Rectangle {
+            id: rectangle
+            x: 9
+            y: 9
+            width: 46
+            height: 46
+            color: "#3045b7"
+            radius: 4
+            border.width: 2
+            smooth: true
+            border.color: "#9ea0bb"
+            anchors.bottomMargin: 6
+            anchors.topMargin: 9
+            anchors.top: parent.top
+            anchors.bottom: parent.bottom
+        }
+    }
+    states: [
+        State {
+            name: "moved"
+
+            PropertyChanges {
+                target: rectangle
+                x: 567
+                y: 9
+                anchors.bottomMargin: 6
+                anchors.topMargin: 9
+            }
+        }
+    ]
+
+    transitions: [
+        Transition {
+            from: ""
+            to: "moved"
+            SequentialAnimation {
+                PropertyAnimation {
+                    easing: editor.easingCurve
+                    property: "x"
+                    duration: spinBox.value
+                }
+            }
+        },
+        Transition {
+            from: "moved"
+            to: ""
+            PropertyAnimation {
+                easing: editor.easingCurve
+                property: "x"
+                duration: spinBox.value
+            }
+
+        }
+    ]
+}
diff --git a/tools/easingcurveeditor/properties.ui b/tools/easingcurveeditor/properties.ui
new file mode 100644 (file)
index 0000000..af96e9c
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Properties</class>
+ <widget class="QWidget" name="Properties">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>487</width>
+    <height>627</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="2">
+    <spacer name="spacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Fixed</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>13</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Duration</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1" colspan="2">
+    <widget class="QSpinBox" name="spinBox"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Code</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1" colspan="2">
+    <widget class="QPlainTextEdit" name="plainTextEdit">
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>128</height>
+      </size>
+     </property>
+     <property name="readOnly">
+      <bool>false</bool>
+     </property>
+     <property name="plainText">
+      <string notr="true"/>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Presets</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1" colspan="2">
+    <widget class="QComboBox" name="comboBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <spacer name="spacer_2">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Fixed</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="5" column="0" colspan="3">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>400</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="title">
+      <string>Control Points</string>
+     </property>
+     <property name="flat">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tools/easingcurveeditor/resources.qrc b/tools/easingcurveeditor/resources.qrc
new file mode 100644 (file)
index 0000000..c184af4
--- /dev/null
@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/">
+        <file>preview.qml</file>
+        <file>Button.qml</file>
+    </qresource>
+</RCC>
diff --git a/tools/easingcurveeditor/segmentproperties.cpp b/tools/easingcurveeditor/segmentproperties.cpp
new file mode 100644 (file)
index 0000000..886d463
--- /dev/null
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "segmentproperties.h"
+#include "splineeditor.h"
+
+SegmentProperties::SegmentProperties(QWidget *parent) :
+    QWidget(parent), m_splineEditor(0), m_blockSignals(false)
+{
+    QVBoxLayout *layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    layout->setSpacing(2);
+    setLayout(layout);
+    {
+        QWidget *widget = new QWidget(this);
+        m_ui_pane_c1.setupUi(widget);
+        m_ui_pane_c1.label->setText("c1");
+        m_ui_pane_c1.smooth->setVisible(false);
+        layout->addWidget(widget);
+
+        connect(m_ui_pane_c1.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
+        connect(m_ui_pane_c1.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
+    }
+    {
+        QWidget *widget = new QWidget(this);
+        m_ui_pane_c2.setupUi(widget);
+        m_ui_pane_c2.label->setText("c2");
+        m_ui_pane_c2.smooth->setVisible(false);
+        layout->addWidget(widget);
+
+        connect(m_ui_pane_c2.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
+        connect(m_ui_pane_c2.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
+    }
+    {
+        QWidget *widget = new QWidget(this);
+        m_ui_pane_p.setupUi(widget);
+        m_ui_pane_p.label->setText("p1");
+        layout->addWidget(widget);
+
+        connect(m_ui_pane_p.smooth, SIGNAL(toggled(bool)), this, SLOT(pUpdated()));
+        connect(m_ui_pane_p.p1_x, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
+        connect(m_ui_pane_p.p1_y, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
+    }
+}
+
+void SegmentProperties::c1Updated()
+{
+    if (m_splineEditor && !m_blockSignals) {
+        QPointF c1(m_ui_pane_c1.p1_x->value(), m_ui_pane_c1.p1_y->value());
+        m_splineEditor->setControlPoint(m_segment * 3, c1);
+    }
+}
+
+void SegmentProperties::c2Updated()
+{
+    if (m_splineEditor && !m_blockSignals) {
+        QPointF c2(m_ui_pane_c2.p1_x->value(), m_ui_pane_c2.p1_y->value());
+        m_splineEditor->setControlPoint(m_segment * 3 + 1, c2);
+    }
+}
+
+void SegmentProperties::pUpdated()
+{
+    if (m_splineEditor && !m_blockSignals) {
+        QPointF p(m_ui_pane_p.p1_x->value(), m_ui_pane_p.p1_y->value());
+        bool smooth = m_ui_pane_p.smooth->isChecked();
+        m_splineEditor->setControlPoint(m_segment * 3 + 2, p);
+        m_splineEditor->setSmooth(m_segment, smooth);
+    }
+}
+
+void SegmentProperties::invalidate()
+{
+    m_blockSignals = true;
+
+     m_ui_pane_p.label->setText(QLatin1String("p") + QString::number(m_segment));
+     m_ui_pane_p.smooth->setChecked(m_smooth);
+     m_ui_pane_p.smooth->parentWidget()->setEnabled(!m_last);
+
+     m_ui_pane_c1.p1_x->setValue(m_points.at(0).x());
+     m_ui_pane_c1.p1_y->setValue(m_points.at(0).y());
+
+     m_ui_pane_c2.p1_x->setValue(m_points.at(1).x());
+     m_ui_pane_c2.p1_y->setValue(m_points.at(1).y());
+
+     m_ui_pane_p.p1_x->setValue(m_points.at(2).x());
+     m_ui_pane_p.p1_y->setValue(m_points.at(2).y());
+
+     m_blockSignals = false;
+}
diff --git a/tools/easingcurveeditor/segmentproperties.h b/tools/easingcurveeditor/segmentproperties.h
new file mode 100644 (file)
index 0000000..4d86be3
--- /dev/null
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SEGMENTPROPERTIES_H
+#define SEGMENTPROPERTIES_H
+
+#include <QWidget>
+#include <ui_pane.h>
+
+class SplineEditor;
+
+class SegmentProperties : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit SegmentProperties(QWidget *parent = 0);
+    void setSplineEditor(SplineEditor *splineEditor)
+    {
+        m_splineEditor = splineEditor;
+    }
+
+    void setSegment(int segment, QVector<QPointF> points, bool smooth, bool last)
+    {
+        m_segment = segment;
+        m_points = points;
+        m_smooth = smooth;
+        m_last = last;
+        invalidate();
+    }
+
+signals:
+
+public slots:
+
+private slots:
+    void c1Updated();
+    void c2Updated();
+    void pUpdated();
+
+private:
+    void invalidate();
+
+    Ui_Pane m_ui_pane_c1;
+    Ui_Pane m_ui_pane_c2;
+    Ui_Pane m_ui_pane_p;
+
+    SplineEditor *m_splineEditor;
+    QVector<QPointF> m_points;
+    int m_segment;
+    bool m_smooth;
+    bool m_last;
+
+    bool m_blockSignals;
+};
+
+#endif // SEGMENTPROPERTIES_H
diff --git a/tools/easingcurveeditor/splineeditor.cpp b/tools/easingcurveeditor/splineeditor.cpp
new file mode 100644 (file)
index 0000000..dbd32dd
--- /dev/null
@@ -0,0 +1,714 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "splineeditor.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+#include <QContextMenuEvent>
+#include <QDebug>
+#include <QApplication>
+#include <segmentproperties.h>
+
+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.cubicBezierSpline().toVector();
+    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();
+            }
+        }
+    }
+}
diff --git a/tools/easingcurveeditor/splineeditor.h b/tools/easingcurveeditor/splineeditor.h
new file mode 100644 (file)
index 0000000..81f2dfd
--- /dev/null
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPLINEEDITOR_H
+#define SPLINEEDITOR_H
+
+#include <QWidget>
+#include <QMenu>
+#include <QAction>
+#include <QScrollArea>
+
+#include <QEasingCurve>
+#include <QHash>
+
+class SegmentProperties;
+
+class SplineEditor : public QWidget
+{
+    Q_OBJECT
+
+     Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve NOTIFY easingCurveChanged);
+
+public:
+    explicit SplineEditor(QWidget *parent = 0);
+    QString generateCode();
+    QStringList presetNames() const;
+    QWidget *pointListWidget();
+
+    void setControlPoint(int index, const QPointF &point)
+    {
+        m_controlPoints[index] = point;
+        update();
+    }
+
+    void setSmooth(int index, bool smooth)
+    {
+        m_smoothAction->setChecked(smooth);
+        smoothPoint(index * 3 + 2);
+        //update();
+    }
+
+signals:
+    void easingCurveChanged();
+    void easingCurveCodeChanged(const QString &code);
+
+
+public slots:
+    void setEasingCurve(const QEasingCurve &easingCurve);
+    void setPreset(const QString &name);
+    void setEasingCurve(const QString &code);
+
+protected:
+    void paintEvent(QPaintEvent *);
+    void mousePressEvent(QMouseEvent *);
+    void mouseMoveEvent(QMouseEvent *);
+    void mouseReleaseEvent(QMouseEvent *);
+    void contextMenuEvent(QContextMenuEvent *);
+
+    void invalidate();
+    void invalidateSmoothList();
+    void invalidateSegmentProperties();
+
+    QEasingCurve easingCurve() const
+    { return m_easingCurve; }
+
+    QHash<QString, QEasingCurve> presets() const;
+
+private:
+    int findControlPoint(const QPoint &point);
+    bool isSmooth(int i) const;
+
+    void smoothPoint( int index);
+    void cornerPoint( int index);
+    void deletePoint(int index);
+    void addPoint(const QPointF point);
+
+    void initPresets();
+
+    void setupPointListWidget();
+
+    bool isControlPointSmooth(int i) const;
+
+    QEasingCurve m_easingCurve;
+    QVector<QPointF> m_controlPoints;
+    QVector<bool> m_smoothList;
+    int m_numberOfSegments;
+    int m_activeControlPoint;
+    bool m_mouseDrag;
+    QPoint m_mousePress;
+    QHash<QString, QEasingCurve> m_presets;
+
+    QMenu *m_pointContextMenu;
+    QMenu *m_curveContextMenu;
+    QAction *m_deleteAction;
+    QAction *m_smoothAction;
+    QAction *m_cornerAction;
+    QAction *m_addPoint;
+
+    QScrollArea *m_pointListWidget;
+
+    QList<SegmentProperties*> m_segmentProperties;
+    bool m_block;
+};
+
+#endif // SPLINEEDITOR_H
index 34e90ed..186c7e9 100644 (file)
@@ -1,5 +1,5 @@
 TEMPLATE = subdirs
-SUBDIRS +=  qmlscene qmlplugindump qmlmin qmleasing qmlprofiler
+SUBDIRS +=  qmlscene qmlplugindump qmlmin qmleasing qmlprofiler easingcurveeditor
 
 contains(QT_CONFIG, qmltest): SUBDIRS += qmltestrunner