From: Thomas Hartmann Date: Tue, 13 Mar 2012 09:27:59 +0000 (+0100) Subject: Adding a custom easing curve editor to declarative tools X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=a920803631d1788dc563f243a3780899d1ac2af2;p=konrad%2Fqtdeclarative.git Adding a custom easing curve editor to declarative tools Change-Id: Ic8ef77792d74ec99b23d85cd8888e0190acc3e10 Reviewed-by: Thomas Hartmann Reviewed-by: Alessandro Portale --- diff --git a/tools/easingcurveeditor/Button.qml b/tools/easingcurveeditor/Button.qml new file mode 100644 index 0000000..a47809a --- /dev/null +++ b/tools/easingcurveeditor/Button.qml @@ -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 index 0000000..0a266d0 --- /dev/null +++ b/tools/easingcurveeditor/easingcurveeditor.pro @@ -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 index 0000000..05e8ace --- /dev/null +++ b/tools/easingcurveeditor/main.cpp @@ -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 +#include + + +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 index 0000000..569b74a --- /dev/null +++ b/tools/easingcurveeditor/mainwindow.cpp @@ -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 +#include +#include +#include +#include +#include + +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 index 0000000..44591d3 --- /dev/null +++ b/tools/easingcurveeditor/mainwindow.h @@ -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 +#include +#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 index 0000000..1500589 --- /dev/null +++ b/tools/easingcurveeditor/pane.ui @@ -0,0 +1,112 @@ + + + Pane + + + + 0 + 0 + 416 + 47 + + + + + 0 + 0 + + + + Form + + + + + + + 75 + true + + + + p1 + + + + + + + + 4 + + + 2 + + + + + x + + + + + + + 4 + + + 0.010000000000000 + + + + + + + y + + + + + + + 4 + + + -10.000000000000000 + + + 10.000000000000000 + + + 0.010000000000000 + + + + + + + + + + smooth + + + + + + + Qt::Horizontal + + + + 99 + 10 + + + + + + + + + diff --git a/tools/easingcurveeditor/preview.qml b/tools/easingcurveeditor/preview.qml new file mode 100644 index 0000000..61f4862 --- /dev/null +++ b/tools/easingcurveeditor/preview.qml @@ -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 index 0000000..af96e9c --- /dev/null +++ b/tools/easingcurveeditor/properties.ui @@ -0,0 +1,131 @@ + + + Properties + + + + 0 + 0 + 487 + 627 + + + + + 0 + 0 + + + + Form + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 13 + + + + + + + + Duration + + + + + + + + + + Code + + + + + + + + 16777215 + 128 + + + + false + + + + + + + + + + Presets + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 0 + + + + Control Points + + + true + + + + + + + + diff --git a/tools/easingcurveeditor/resources.qrc b/tools/easingcurveeditor/resources.qrc new file mode 100644 index 0000000..c184af4 --- /dev/null +++ b/tools/easingcurveeditor/resources.qrc @@ -0,0 +1,6 @@ + + + preview.qml + Button.qml + + diff --git a/tools/easingcurveeditor/segmentproperties.cpp b/tools/easingcurveeditor/segmentproperties.cpp new file mode 100644 index 0000000..886d463 --- /dev/null +++ b/tools/easingcurveeditor/segmentproperties.cpp @@ -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 index 0000000..4d86be3 --- /dev/null +++ b/tools/easingcurveeditor/segmentproperties.h @@ -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 +#include + +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 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 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 index 0000000..dbd32dd --- /dev/null +++ b/tools/easingcurveeditor/splineeditor.cpp @@ -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 +#include +#include +#include +#include +#include + +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 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; iisChecked()) { + + 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 realList; + foreach (const QString &string, stringList) { + bool ok; + realList.append(string.toDouble(&ok)); + if (!ok) + return; + } + QList 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 index 0000000..81f2dfd --- /dev/null +++ b/tools/easingcurveeditor/splineeditor.h @@ -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 +#include +#include +#include + +#include +#include + +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 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 m_controlPoints; + QVector m_smoothList; + int m_numberOfSegments; + int m_activeControlPoint; + bool m_mouseDrag; + QPoint m_mousePress; + QHash m_presets; + + QMenu *m_pointContextMenu; + QMenu *m_curveContextMenu; + QAction *m_deleteAction; + QAction *m_smoothAction; + QAction *m_cornerAction; + QAction *m_addPoint; + + QScrollArea *m_pointListWidget; + + QList m_segmentProperties; + bool m_block; +}; + +#endif // SPLINEEDITOR_H diff --git a/tools/tools.pro b/tools/tools.pro index 34e90ed..186c7e9 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -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