summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKnud Dollereder <knud.dollereder@qt.io>2019-05-24 15:03:51 +0200
committerKnud Dollereder <knud.dollereder@qt.io>2019-05-29 14:42:32 +0000
commit69e0ce6b1aee59585f141a320cef4f980e6ae7dc (patch)
tree4dcd6b41099fb621dc205cc0ee28cedc91672fbc
parentdf6afb91041d5225448a13aaf3bbb6f11137b0c0 (diff)
Implement selection of keyframes and handles
Change-Id: Iaedb5561f9d57e38bfcac6fa5012a26ac5033d3b Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--src/curveeditor/curveeditor.cpp4
-rw-r--r--src/curveeditor/curveeditor.pri18
-rw-r--r--src/curveeditor/curveeditorstyle.h12
-rw-r--r--src/curveeditor/detail/curveeditorstyledialog.cpp2
-rw-r--r--src/curveeditor/detail/curveitem.cpp93
-rw-r--r--src/curveeditor/detail/curveitem.h17
-rw-r--r--src/curveeditor/detail/graphicsscene.cpp81
-rw-r--r--src/curveeditor/detail/graphicsscene.h18
-rw-r--r--src/curveeditor/detail/graphicsview.cpp120
-rw-r--r--src/curveeditor/detail/graphicsview.h39
-rw-r--r--src/curveeditor/detail/handleitem.cpp30
-rw-r--r--src/curveeditor/detail/handleitem.h12
-rw-r--r--src/curveeditor/detail/keyframeitem.cpp102
-rw-r--r--src/curveeditor/detail/keyframeitem.h23
-rw-r--r--src/curveeditor/detail/playhead.cpp28
-rw-r--r--src/curveeditor/detail/selectableitem.cpp112
-rw-r--r--src/curveeditor/detail/selectableitem.h85
-rw-r--r--src/curveeditor/detail/selector.cpp219
-rw-r--r--src/curveeditor/detail/selector.h86
-rw-r--r--src/curveeditor/detail/shortcut.cpp81
-rw-r--r--src/curveeditor/detail/shortcut.h61
-rw-r--r--src/curveeditor/detail/utils.h2
22 files changed, 1018 insertions, 227 deletions
diff --git a/src/curveeditor/curveeditor.cpp b/src/curveeditor/curveeditor.cpp
index 8501041..4eba31c 100644
--- a/src/curveeditor/curveeditor.cpp
+++ b/src/curveeditor/curveeditor.cpp
@@ -53,12 +53,12 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
void CurveEditor::zoomX(double zoom)
{
- m_view->zoomX(zoom);
+ m_view->setZoomX(zoom);
}
void CurveEditor::zoomY(double zoom)
{
- m_view->zoomY(zoom);
+ m_view->setZoomY(zoom);
}
} // End namespace DesignTools.
diff --git a/src/curveeditor/curveeditor.pri b/src/curveeditor/curveeditor.pri
index f65a34a..31ffe5d 100644
--- a/src/curveeditor/curveeditor.pri
+++ b/src/curveeditor/curveeditor.pri
@@ -4,8 +4,6 @@ HEADERS += \
$$PWD/animationcurve.h \
$$PWD/curveeditor.h \
$$PWD/curveeditormodel.h \
- $$PWD/keyframe.h \
- $$PWD/treeitem.h \
$$PWD/detail/colorcontrol.h \
$$PWD/detail/curveeditorstyledialog.h \
$$PWD/detail/curveitem.h \
@@ -15,16 +13,19 @@ HEADERS += \
$$PWD/detail/handleitem.h \
$$PWD/detail/keyframeitem.h \
$$PWD/detail/playhead.h \
+ $$PWD/detail/selectableitem.h \
+ $$PWD/detail/selector.h \
+ $$PWD/detail/shortcut.h \
$$PWD/detail/treeitemdelegate.h \
$$PWD/detail/treemodel.h \
- $$PWD/detail/treeview.h
+ $$PWD/detail/treeview.h \
+ $$PWD/keyframe.h \
+ $$PWD/treeitem.h
SOURCES += \
$$PWD/animationcurve.cpp \
$$PWD/curveeditor.cpp \
$$PWD/curveeditormodel.cpp \
- $$PWD/keyframe.cpp \
- $$PWD/treeitem.cpp \
$$PWD/detail/colorcontrol.cpp \
$$PWD/detail/curveeditorstyledialog.cpp \
$$PWD/detail/curveitem.cpp \
@@ -34,7 +35,12 @@ SOURCES += \
$$PWD/detail/handleitem.cpp \
$$PWD/detail/keyframeitem.cpp \
$$PWD/detail/playhead.cpp \
+ $$PWD/detail/selectableitem.cpp \
+ $$PWD/detail/selector.cpp \
+ $$PWD/detail/shortcut.cpp \
$$PWD/detail/treeitemdelegate.cpp \
$$PWD/detail/treemodel.cpp \
$$PWD/detail/treeview.cpp \
- $$PWD/detail/utils.cpp
+ $$PWD/detail/utils.cpp \
+ $$PWD/keyframe.cpp \
+ $$PWD/treeitem.cpp
diff --git a/src/curveeditor/curveeditorstyle.h b/src/curveeditor/curveeditorstyle.h
index ed82317..03ea11c 100644
--- a/src/curveeditor/curveeditorstyle.h
+++ b/src/curveeditor/curveeditorstyle.h
@@ -25,6 +25,8 @@
#pragma once
+#include "detail/shortcut.h"
+
#include <QBitmap>
#include <QBrush>
#include <QColor>
@@ -74,8 +76,14 @@ struct PlayheadStyleOption
struct Shortcuts
{
- QKeySequence zoom = QKeySequence(Qt::AltModifier, Qt::RightButton);
- QKeySequence pan = QKeySequence(Qt::AltModifier, Qt::MiddleButton);
+ Shortcut newSelection = Shortcut(Qt::LeftButton);
+ Shortcut addToSelection = Shortcut(Qt::LeftButton, Qt::ControlModifier | Qt::ShiftModifier);
+ Shortcut removeFromSelection = Shortcut(Qt::LeftButton, Qt::ShiftModifier);
+ Shortcut toggleSelection = Shortcut(Qt::LeftButton, Qt::ControlModifier);
+
+ Shortcut zoom = Shortcut(Qt::RightButton, Qt::AltModifier);
+ Shortcut pan = Shortcut(Qt::MiddleButton, Qt::AltModifier);
+ Shortcut frameAll = Shortcut(Qt::NoModifier, Qt::Key_A);
};
struct CurveEditorStyle
diff --git a/src/curveeditor/detail/curveeditorstyledialog.cpp b/src/curveeditor/detail/curveeditorstyledialog.cpp
index 7f2e288..a8b653a 100644
--- a/src/curveeditor/detail/curveeditorstyledialog.cpp
+++ b/src/curveeditor/detail/curveeditorstyledialog.cpp
@@ -84,6 +84,8 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
m_canvasMargin->setValue(style.canvasMargin);
m_zoomInWidth->setValue(style.zoomInWidth);
m_zoomInHeight->setValue(style.zoomInHeight);
+ m_zoomInHeight->setMaximum(9000);
+
m_timeAxisHeight->setValue(style.timeAxisHeight);
m_timeOffsetLeft->setValue(style.timeOffsetLeft);
m_timeOffsetRight->setValue(style.timeOffsetRight);
diff --git a/src/curveeditor/detail/curveitem.cpp b/src/curveeditor/detail/curveitem.cpp
index 9136989..5973e70 100644
--- a/src/curveeditor/detail/curveitem.cpp
+++ b/src/curveeditor/detail/curveitem.cpp
@@ -24,6 +24,7 @@
****************************************************************************/
#include "curveitem.h"
#include "animationcurve.h"
+#include "graphicsscene.h"
#include "keyframeitem.h"
#include "utils.h"
@@ -38,8 +39,7 @@ CurveItem::CurveItem(QGraphicsItem *parent)
, m_style()
, m_transform()
, m_keyframes()
- , m_selected(false)
- , m_preselected(false)
+ , m_underMouse(false)
, m_itemDirty(false)
, m_pathDirty(true)
{}
@@ -50,13 +50,14 @@ CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem
, m_style()
, m_transform()
, m_keyframes()
- , m_selected(false)
- , m_preselected(false)
+ , m_underMouse(false)
, m_itemDirty(false)
, m_pathDirty(true)
{
setAcceptHoverEvents(true);
+ setFlag(QGraphicsItem::ItemIsMovable, false);
+
auto emitCurveChanged = [this]() {
m_itemDirty = true;
m_pathDirty = true;
@@ -65,7 +66,7 @@ CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem
for (auto frame : curve.keyframes()) {
auto *item = new KeyframeItem(frame, this);
- connect(item, &KeyframeItem::keyframeChanged, emitCurveChanged);
+ QObject::connect(item, &KeyframeItem::redrawCurve, emitCurveChanged);
m_keyframes.push_back(item);
}
}
@@ -109,8 +110,10 @@ void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidg
{
if (m_keyframes.size() > 1) {
QPen pen = painter->pen();
+ QColor col = m_underMouse ? Qt::red : m_style.color;
+
pen.setWidthF(m_style.width);
- pen.setColor(m_selected ? m_style.selectionColor : (m_preselected ? Qt::red : m_style.color));
+ pen.setColor(hasSelection() ? m_style.selectionColor : col);
painter->save();
painter->setPen(pen);
@@ -125,11 +128,48 @@ bool CurveItem::isDirty() const
return m_itemDirty;
}
+bool CurveItem::hasSelection() const
+{
+ for (auto *frame : m_keyframes) {
+ if (frame->selected())
+ return true;
+ }
+
+ return false;
+}
+
unsigned int CurveItem::id() const
{
return m_id;
}
+QPainterPath CurveItem::path() const
+{
+ if (m_pathDirty) {
+ Keyframe previous = m_keyframes.front()->keyframe();
+ Keyframe current;
+
+ m_path = QPainterPath(m_transform.map(previous.position()));
+ for (size_t i = 1; i < m_keyframes.size(); ++i) {
+ current = m_keyframes[i]->keyframe();
+
+ if (previous.rightHandle().isNull() || current.leftHandle().isNull()) {
+ m_path.lineTo(m_transform.map(current.position()));
+ } else {
+ m_path.cubicTo(
+ m_transform.map(previous.rightHandle()),
+ m_transform.map(current.leftHandle()),
+ m_transform.map(current.position()));
+ }
+
+ previous = current;
+ }
+ m_pathDirty = false;
+ }
+
+ return m_path;
+}
+
AnimationCurve CurveItem::curve() const
{
std::vector<Keyframe> out;
@@ -165,45 +205,18 @@ void CurveItem::setStyle(const CurveEditorStyle &style)
frame->setStyle(style);
}
-QPainterPath CurveItem::path() const
+void CurveItem::connect(GraphicsScene *scene)
{
- if (m_pathDirty) {
- Keyframe previous = m_keyframes.front()->keyframe();
- Keyframe current;
-
- m_path = QPainterPath(m_transform.map(previous.position()));
- for (size_t i = 1; i < m_keyframes.size(); ++i) {
- current = m_keyframes[i]->keyframe();
-
- if (previous.rightHandle().isNull() || current.leftHandle().isNull()) {
- m_path.lineTo(m_transform.map(current.position()));
- } else {
- m_path.cubicTo(
- m_transform.map(previous.rightHandle()),
- m_transform.map(current.leftHandle()),
- m_transform.map(current.position()));
- }
-
- previous = current;
- }
- m_pathDirty = false;
- }
-
- return m_path;
-}
-
-void CurveItem::setSelected(bool select)
-{
- if (select != m_selected) {
- m_selected = select;
- update();
+ for (auto *frame : m_keyframes) {
+ QObject::connect(frame, &KeyframeItem::keyframeMoved, scene, &GraphicsScene::keyframeMoved);
+ QObject::connect(frame, &KeyframeItem::handleMoved, scene, &GraphicsScene::handleMoved);
}
}
-void CurveItem::setPreSelected(bool preSelect)
+void CurveItem::setIsUnderMouse(bool under)
{
- if (preSelect != m_preselected) {
- m_preselected = preSelect;
+ if (under != m_underMouse) {
+ m_underMouse = under;
update();
}
}
diff --git a/src/curveeditor/detail/curveitem.h b/src/curveeditor/detail/curveitem.h
index cf9b29f..90e68e2 100644
--- a/src/curveeditor/detail/curveitem.h
+++ b/src/curveeditor/detail/curveitem.h
@@ -26,6 +26,7 @@
#pragma once
#include "curveeditorstyle.h"
+#include "selectableitem.h"
#include <QGraphicsObject>
@@ -33,6 +34,7 @@ namespace DesignTools {
class AnimationCurve;
class KeyframeItem;
+class GraphicsScene;
class CurveItem : public QGraphicsObject
{
@@ -45,7 +47,7 @@ public:
~CurveItem() override;
- enum { Type = UserType + 1 };
+ enum { Type = ItemTypeCurve };
int type() const override;
@@ -53,10 +55,12 @@ public:
bool contains(const QPointF &point) const override;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
bool isDirty() const;
+ bool hasSelection() const;
+
unsigned int id() const;
AnimationCurve curve() const;
@@ -67,14 +71,13 @@ public:
void setStyle(const CurveEditorStyle &style);
- void setSelected(bool select);
+ void connect(GraphicsScene *scene);
- void setPreSelected(bool preSelect);
+ void setIsUnderMouse(bool under);
private:
QPainterPath path() const;
-private:
unsigned int m_id;
CurveItemStyleOption m_style;
@@ -83,9 +86,7 @@ private:
std::vector<KeyframeItem *> m_keyframes;
- bool m_selected;
-
- bool m_preselected;
+ bool m_underMouse;
bool m_itemDirty;
diff --git a/src/curveeditor/detail/graphicsscene.cpp b/src/curveeditor/detail/graphicsscene.cpp
index 3fd2b71..096e57a 100644
--- a/src/curveeditor/detail/graphicsscene.cpp
+++ b/src/curveeditor/detail/graphicsscene.cpp
@@ -27,6 +27,7 @@
#include "animationcurve.h"
#include "curveitem.h"
#include "graphicsview.h"
+#include "handleitem.h"
#include <QGraphicsSceneMouseEvent>
@@ -67,6 +68,7 @@ void GraphicsScene::addCurveItem(CurveItem *item)
{
m_dirty = true;
addItem(item);
+ item->connect(this);
}
void GraphicsScene::setComponentTransform(const QTransform &transform)
@@ -87,14 +89,48 @@ void GraphicsScene::setComponentTransform(const QTransform &transform)
setSceneRect(bounds);
}
+void GraphicsScene::keyframeMoved(KeyframeItem *movedItem, const QPointF &direction)
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (item == movedItem)
+ continue;
+
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (frameItem->selected())
+ frameItem->moveKeyframe(direction);
+ }
+ }
+}
+
+void GraphicsScene::handleMoved(KeyframeItem *frame,
+ HandleSlot handle,
+ double angle,
+ double deltaLength)
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (item == frame)
+ continue;
+
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (frameItem->selected())
+ frameItem->moveHandle(handle, angle, deltaLength);
+ }
+ }
+}
+
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
+ if (hasActiveItem())
+ return;
+
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
- curveItem->setPreSelected(curveItem->contains(mouseEvent->scenePos()));
+ curveItem->setIsUnderMouse(curveItem->contains(mouseEvent->scenePos()));
}
}
@@ -102,8 +138,6 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseReleaseEvent(mouseEvent);
- clearSelection();
-
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
@@ -111,14 +145,43 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
curveItem->setSelected(true);
if (curveItem->isDirty()) {
- m_dirty = true;
emit curveChanged(curveItem->id(), curveItem->curve());
curveItem->setDirty(false);
+ m_dirty = true;
}
}
}
}
+bool GraphicsScene::hasActiveKeyframe() const
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *kitem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (kitem->activated())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicsScene::hasActiveHandle() const
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *hitem = qgraphicsitem_cast<HandleItem *>(item)) {
+ if (hitem->activated())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicsScene::hasActiveItem() const
+{
+ return hasActiveKeyframe() || hasActiveHandle();
+}
+
GraphicsView *GraphicsScene::graphicsView() const
{
const QList<QGraphicsView *> viewList = views();
@@ -159,14 +222,4 @@ QRectF GraphicsScene::limits() const
return m_limits;
}
-void GraphicsScene::clearSelection()
-{
- const auto itemList = items();
- for (auto *item : itemList) {
- if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
- curveItem->setSelected(false);
- }
- }
-}
-
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/graphicsscene.h b/src/curveeditor/detail/graphicsscene.h
index 4725d33..981c326 100644
--- a/src/curveeditor/detail/graphicsscene.h
+++ b/src/curveeditor/detail/graphicsscene.h
@@ -25,6 +25,8 @@
#pragma once
+#include "keyframeitem.h"
+
#include <QGraphicsScene>
namespace DesignTools {
@@ -45,6 +47,12 @@ public:
bool empty() const;
+ bool hasActiveKeyframe() const;
+
+ bool hasActiveHandle() const;
+
+ bool hasActiveItem() const;
+
double minimumTime() const;
double maximumTime() const;
@@ -55,7 +63,11 @@ public:
void addCurveItem(CurveItem *item);
- void setComponentTransform(const QTransform& transform);
+ void setComponentTransform(const QTransform &transform);
+
+ void keyframeMoved(KeyframeItem *item, const QPointF &direction);
+
+ void handleMoved(KeyframeItem *frame, HandleSlot handle, double angle, double deltaLength);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
@@ -65,12 +77,10 @@ protected:
private:
using QGraphicsScene::addItem;
- GraphicsView * graphicsView() const;
+ GraphicsView *graphicsView() const;
QRectF limits() const;
- void clearSelection();
-
mutable bool m_dirty;
mutable QRectF m_limits;
diff --git a/src/curveeditor/detail/graphicsview.cpp b/src/curveeditor/detail/graphicsview.cpp
index 245194f..ae60888 100644
--- a/src/curveeditor/detail/graphicsview.cpp
+++ b/src/curveeditor/detail/graphicsview.cpp
@@ -29,8 +29,6 @@
#include "utils.h"
#include <QAction>
-#include <QApplication>
-#include <QKeySequence>
#include <QMenu>
#include <QResizeEvent>
#include <QScrollBar>
@@ -41,13 +39,15 @@ namespace DesignTools {
GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
: QGraphicsView(parent)
+ , m_zoomX(0.0)
+ , m_zoomY(0.0)
+ , m_transform()
, m_scene()
- , m_style(model->style())
, m_model(model)
, m_playhead(this)
+ , m_selector()
+ , m_style(model->style())
, m_dialog(m_style)
- , m_zoomX(0.0)
- , m_zoomY(0.0)
{
model->setGraphicsView(this);
@@ -82,6 +82,16 @@ CurveEditorStyle GraphicsView::editorStyle() const
return m_style;
}
+bool GraphicsView::hasActiveItem() const
+{
+ return m_scene.hasActiveItem();
+}
+
+bool GraphicsView::hasActiveHandle() const
+{
+ return m_scene.hasActiveHandle();
+}
+
double GraphicsView::minimumTime() const
{
bool check = m_model->minimumTime() < m_scene.minimumTime();
@@ -104,6 +114,16 @@ double GraphicsView::maximumValue() const
return m_scene.empty() ? 1.0 : m_scene.maximumValue();
}
+double GraphicsView::zoomX() const
+{
+ return m_zoomX;
+}
+
+double GraphicsView::zoomY() const
+{
+ return m_zoomY;
+}
+
QRectF GraphicsView::canvasRect() const
{
QRect r = viewport()->rect().adjusted(
@@ -152,15 +172,15 @@ void GraphicsView::setStyle(const CurveEditorStyle &style)
viewport()->update();
}
-void GraphicsView::zoomX(double zoom)
+void GraphicsView::setZoomX(double zoom, const QPoint &pivot)
{
- applyZoom(zoom, m_zoomY);
+ applyZoom(zoom, m_zoomY, pivot);
viewport()->update();
}
-void GraphicsView::zoomY(double zoom)
+void GraphicsView::setZoomY(double zoom, const QPoint &pivot)
{
- applyZoom(m_zoomX, zoom);
+ applyZoom(m_zoomX, zoom, pivot);
viewport()->update();
}
@@ -171,6 +191,14 @@ void GraphicsView::setCurrentFrame(int frame)
viewport()->update();
}
+void GraphicsView::scrollContent(double x, double y)
+{
+ QScrollBar *hs = horizontalScrollBar();
+ QScrollBar *vs = verticalScrollBar();
+ hs->setValue(hs->value() + x);
+ vs->setValue(vs->value() + y);
+}
+
void GraphicsView::reset(const std::vector<CurveItem *> &items)
{
m_scene.clear();
@@ -187,63 +215,50 @@ void GraphicsView::resizeEvent(QResizeEvent *event)
applyZoom(m_zoomX, m_zoomY);
}
+void GraphicsView::keyPressEvent(QKeyEvent *event)
+{
+ Shortcut shortcut(event->modifiers(), static_cast<Qt::Key>(event->key()));
+ if (shortcut == m_style.shortcuts.frameAll)
+ applyZoom(0.0, 0.0);
+}
+
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
- if (event->button() == Qt::LeftButton && event->modifiers().testFlag(Qt::NoModifier)) {
+ if (m_playhead.mousePress(globalToScene(event->globalPos())))
+ return;
+ Shortcut shortcut(event);
+ if (shortcut == Shortcut(Qt::LeftButton)) {
QPointF pos = mapToScene(event->pos());
- if (timeScaleRect().contains(pos))
+ if (timeScaleRect().contains(pos)) {
setCurrentFrame(std::round(mapXtoTime(pos.x())));
+ event->accept();
+ return;
+ }
}
- QKeySequence eventSequence(event->modifiers(), event->buttons());
- if (eventSequence == m_style.shortcuts.zoom || eventSequence == m_style.shortcuts.pan) {
+ QGraphicsView::mousePressEvent(event);
- m_mouse = event->globalPos();
- m_start = event->globalPos();
- event->accept();
- return;
- }
-
- if (!m_playhead.mousePress(globalToScene(event->globalPos())))
- QGraphicsView::mousePressEvent(event);
+ m_selector.mousePress(event, this);
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
- QPointF delta = event->globalPos() - m_mouse;
- if (!m_mouse.isNull() && delta.manhattanLength() >= QApplication::startDragDistance()) {
-
- QKeySequence eventSequence(event->modifiers(), event->buttons());
-
- if (eventSequence == m_style.shortcuts.zoom) {
-
- double bigger = std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y();
- double factor = bigger / this->width();
- double zoomX = m_zoomX + factor;
- applyZoom(zoomX, m_zoomY, m_start);
- m_mouse = event->globalPos();
- return;
-
- } else if (eventSequence == m_style.shortcuts.pan) {
+ if (m_playhead.mouseMove(globalToScene(event->globalPos()), this))
+ return;
- scrollContent(-delta.x(), delta.y());
- m_playhead.resize(this);
- m_mouse = event->globalPos();
- return;
- }
- }
+ QGraphicsView::mouseMoveEvent(event);
- if (!m_playhead.mouseMove(globalToScene(event->globalPos()), this))
- QGraphicsView::mouseMoveEvent(event);
+ m_selector.mouseMove(event, this, m_playhead);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
- m_mouse = QPoint();
- m_start = QPoint();
QGraphicsView::mouseReleaseEvent(event);
+
m_playhead.mouseRelease(this);
+ m_selector.mouseRelease(event, this);
+ this->viewport()->update();
}
void GraphicsView::wheelEvent(QWheelEvent *event)
@@ -280,7 +295,10 @@ void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
m_playhead.paint(painter, this);
- painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()), m_style.backgroundAlternateBrush);
+ painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()),
+ m_style.backgroundAlternateBrush);
+
+ m_selector.paint(painter);
}
void GraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
@@ -324,14 +342,6 @@ QPointF GraphicsView::globalToRaster(const QPoint &point) const
return QPointF(mapXtoTime(scene.x()), mapYtoValue(scene.y()));
}
-void GraphicsView::scrollContent(double x, double y)
-{
- auto *hs = horizontalScrollBar();
- auto *vs = verticalScrollBar();
- hs->setValue(hs->value() + x);
- vs->setValue(vs->value() + y);
-}
-
void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
{
QPointF pivotRaster(globalToRaster(pivot));
diff --git a/src/curveeditor/detail/graphicsview.h b/src/curveeditor/detail/graphicsview.h
index 057476b..1975e36 100644
--- a/src/curveeditor/detail/graphicsview.h
+++ b/src/curveeditor/detail/graphicsview.h
@@ -29,6 +29,7 @@
#include "curveeditorstyledialog.h"
#include "graphicsscene.h"
#include "playhead.h"
+#include "selector.h"
#include <QGraphicsView>
@@ -51,6 +52,10 @@ public:
CurveEditorStyle editorStyle() const;
+ bool hasActiveItem() const;
+
+ bool hasActiveHandle() const;
+
int mapTimeToX(double time) const;
int mapValueToY(double value) const;
@@ -71,6 +76,10 @@ public:
double maximumValue() const;
+ double zoomX() const;
+
+ double zoomY() const;
+
QRectF canvasRect() const;
QRectF timeScaleRect() const;
@@ -81,17 +90,21 @@ public:
void setStyle(const CurveEditorStyle &style);
- void zoomX(double zoom);
+ void setZoomX(double zoom, const QPoint &pivot = QPoint());
- void zoomY(double zoom);
+ void setZoomY(double zoom, const QPoint &pivot = QPoint());
void setCurrentFrame(int frame);
+ void scrollContent(double x, double y);
+
void reset(const std::vector<CurveItem *> &items);
protected:
void resizeEvent(QResizeEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
@@ -107,8 +120,6 @@ protected:
void drawBackground(QPainter *painter, const QRectF &rect) override;
private:
- void scrollContent(double x, double y);
-
void applyZoom(double x, double y, const QPoint &pivot = QPoint());
void drawGrid(QPainter *painter, const QRectF &rect);
@@ -124,25 +135,23 @@ private:
double timeLabelInterval(QPainter *painter, double maxTime);
private:
- GraphicsScene m_scene;
-
- CurveEditorStyle m_style;
+ double m_zoomX;
- CurveEditorModel *m_model;
+ double m_zoomY;
- Playhead m_playhead;
+ QTransform m_transform;
- CurveEditorStyleDialog m_dialog;
+ GraphicsScene m_scene;
- double m_zoomX;
+ CurveEditorModel *m_model;
- double m_zoomY;
+ Playhead m_playhead;
- QTransform m_transform;
+ Selector m_selector;
- QPoint m_mouse;
+ CurveEditorStyle m_style;
- QPoint m_start;
+ CurveEditorStyleDialog m_dialog;
};
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/handleitem.cpp b/src/curveeditor/detail/handleitem.cpp
index d9abc77..c54b26e 100644
--- a/src/curveeditor/detail/handleitem.cpp
+++ b/src/curveeditor/detail/handleitem.cpp
@@ -26,8 +26,6 @@
#include "handleitem.h"
#include "utils.h"
-#include <QEvent>
-#include <QGraphicsSceneMouseEvent>
#include <QPainter>
namespace DesignTools {
@@ -37,7 +35,6 @@ struct HandleGeometry
HandleGeometry(const QPointF &pos, const HandleItemStyleOption &style)
{
QPointF topLeft(-style.size / 2.0, -style.size / 2.0);
-
handle = QRectF(topLeft, -topLeft);
toKeyframe = QLineF(QPointF(0.0, 0.0), -pos);
angle = -toKeyframe.angle() + 45.0;
@@ -51,11 +48,9 @@ struct HandleGeometry
};
HandleItem::HandleItem(QGraphicsItem *parent)
- : QGraphicsObject(parent)
+ : SelectableItem(parent)
, m_style()
{
- setFlag(QGraphicsItem::ItemIsMovable, true);
- setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
setFlag(QGraphicsItem::ItemStacksBehindParent, true);
}
@@ -106,27 +101,4 @@ void HandleItem::setStyle(const CurveEditorStyle &style)
m_style = style.handleStyle;
}
-bool HandleItem::sceneEvent(QEvent *event)
-{
- auto intersectsHandle = [this, event]() {
- HandleGeometry geom(pos(), m_style);
-
- QTransform transform;
- transform.rotate(geom.angle);
-
- auto *mousePressEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
- return bbox(geom.handle, transform).contains(mousePressEvent->pos());
- };
-
- switch (event->type()) {
- case QEvent::GraphicsSceneMousePress:
- if (!intersectsHandle())
- event->ignore();
- return true;
-
- default:
- return QGraphicsObject::sceneEvent(event);
- }
-}
-
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/handleitem.h b/src/curveeditor/detail/handleitem.h
index 8e2ffd7..4c9126c 100644
--- a/src/curveeditor/detail/handleitem.h
+++ b/src/curveeditor/detail/handleitem.h
@@ -26,12 +26,11 @@
#pragma once
#include "curveeditorstyle.h"
-
-#include <QGraphicsObject>
+#include "selectableitem.h"
namespace DesignTools {
-class HandleItem : public QGraphicsObject
+class HandleItem : public SelectableItem
{
Q_OBJECT
@@ -40,19 +39,16 @@ public:
~HandleItem() override;
- enum { Type = UserType + 3 };
+ enum { Type = ItemTypeHandle };
int type() const override;
QRectF boundingRect() const override;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void setStyle(const CurveEditorStyle &style);
-protected:
- bool sceneEvent(QEvent *event) override;
-
private:
HandleItemStyleOption m_style;
};
diff --git a/src/curveeditor/detail/keyframeitem.cpp b/src/curveeditor/detail/keyframeitem.cpp
index 867585c..94cdbbc 100644
--- a/src/curveeditor/detail/keyframeitem.cpp
+++ b/src/curveeditor/detail/keyframeitem.cpp
@@ -33,29 +33,27 @@
namespace DesignTools {
KeyframeItem::KeyframeItem(QGraphicsItem *parent)
- : QGraphicsObject(parent)
+ : SelectableItem(parent)
, m_frame()
{}
KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent)
- : QGraphicsObject(parent)
+ : SelectableItem(parent)
, m_transform()
, m_frame(keyframe)
, m_left(keyframe.hasLeftHandle() ? new HandleItem(this) : nullptr)
, m_right(keyframe.hasRightHandle() ? new HandleItem(this) : nullptr)
{
- setFlag(QGraphicsItem::ItemIsMovable, true);
- setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
- setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
-
- connect(this, &QGraphicsObject::xChanged, this, &KeyframeItem::updatePosition);
- connect(this, &QGraphicsObject::yChanged, this, &KeyframeItem::updatePosition);
+ auto updatePosition = [this]() { this->updatePosition(true); };
+ connect(this, &QGraphicsObject::xChanged, updatePosition);
+ connect(this, &QGraphicsObject::yChanged, updatePosition);
if (m_left) {
m_left->setPos(m_frame.leftHandle() - m_frame.position());
auto updateLeftHandle = [this]() { updateHandle(m_left); };
connect(m_left, &QGraphicsObject::xChanged, updateLeftHandle);
connect(m_left, &QGraphicsObject::yChanged, updateLeftHandle);
+ m_left->hide();
}
if (m_right) {
@@ -63,6 +61,7 @@ KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent)
auto updateRightHandle = [this]() { updateHandle(m_right); };
connect(m_right, &QGraphicsObject::xChanged, updateRightHandle);
connect(m_right, &QGraphicsObject::yChanged, updateRightHandle);
+ m_right->hide();
}
setPos(m_frame.position());
@@ -89,7 +88,7 @@ void KeyframeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
painter->save();
painter->setPen(pen);
- painter->setBrush(isSelected() ? m_style.selectionColor : m_style.color);
+ painter->setBrush(selected() ? Qt::red : m_style.color);
painter->drawEllipse(boundingRect());
painter->restore();
@@ -126,7 +125,7 @@ void KeyframeItem::setStyle(const CurveEditorStyle &style)
m_right->setStyle(style);
}
-void KeyframeItem::updatePosition()
+void KeyframeItem::updatePosition(bool update)
{
bool ok = false;
QPointF position = m_transform.inverted(&ok).map(pos());
@@ -134,6 +133,7 @@ void KeyframeItem::updatePosition()
if (!ok)
return;
+ QPointF oldPosition = m_frame.position();
m_frame.setPosition(position);
if (m_left)
@@ -142,7 +142,41 @@ void KeyframeItem::updatePosition()
if (m_right)
updateHandle(m_right, false);
- emit keyframeChanged();
+ if (update) {
+ emit redrawCurve();
+ emit keyframeMoved(this, position - oldPosition);
+ }
+}
+
+void KeyframeItem::moveKeyframe(const QPointF &direction)
+{
+ this->blockSignals(true);
+ setPos(m_transform.map(m_frame.position() + direction));
+ updatePosition(false);
+ this->blockSignals(false);
+ emit redrawCurve();
+}
+
+void KeyframeItem::moveHandle(HandleSlot handle, double deltaAngle, double deltaLength)
+{
+ auto move = [this, deltaAngle, deltaLength](HandleItem *item) {
+ QLineF current(QPointF(0.0, 0.0), item->pos());
+ current.setAngle(current.angle() + deltaAngle);
+ current.setLength(current.length() + deltaLength);
+ item->setPos(current.p2());
+ updateHandle(item, false);
+ };
+
+ this->blockSignals(true);
+
+ if (handle == HandleSlot::Left)
+ move(m_left);
+ else if (handle == HandleSlot::Right)
+ move(m_right);
+
+ this->blockSignals(false);
+
+ emit redrawCurve();
}
void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged)
@@ -154,19 +188,39 @@ void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged)
if (!ok)
return;
- if (handle == m_left)
+ QPointF oldPosition;
+ QPointF newPosition;
+ HandleSlot slot = HandleSlot::Undefined;
+ if (handle == m_left) {
+ slot = HandleSlot::Left;
+ oldPosition = m_frame.leftHandle();
m_frame.setLeftHandle(m_frame.position() + handlePosition);
- else
+ newPosition = m_frame.leftHandle();
+ } else {
+ slot = HandleSlot::Right;
+ oldPosition = m_frame.rightHandle();
m_frame.setRightHandle(m_frame.position() + handlePosition);
+ newPosition = m_frame.rightHandle();
+ }
+
+ if (emitChanged) {
+ QLineF oldLine(m_frame.position(), oldPosition);
+ QLineF newLine(m_frame.position(), newPosition);
+
+ QLineF mappedOld = m_transform.map(oldLine);
+ QLineF mappedNew = m_transform.map(newLine);
+
+ auto angle = mappedOld.angleTo(mappedNew);
+ auto deltaLength = mappedNew.length() - mappedOld.length();
- if (emitChanged)
- emit keyframeChanged();
+ emit redrawCurve();
+ emit handleMoved(this, slot, angle, deltaLength);
+ }
}
QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange) {
- // Snap to keyframe.
bool ok;
QPointF position = m_transform.inverted(&ok).map(value.toPointF());
if (ok) {
@@ -178,4 +232,20 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
return QGraphicsItem::itemChange(change, value);
}
+void KeyframeItem::selectionCallback()
+{
+ auto setHandleVisibility = [](HandleItem *handle, bool visible) {
+ if (handle)
+ handle->setVisible(visible);
+ };
+
+ if (selected()) {
+ setHandleVisibility(m_left, true);
+ setHandleVisibility(m_right, true);
+ } else {
+ setHandleVisibility(m_left, false);
+ setHandleVisibility(m_right, false);
+ }
+}
+
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/keyframeitem.h b/src/curveeditor/detail/keyframeitem.h
index 88eb366..ae65be1 100644
--- a/src/curveeditor/detail/keyframeitem.h
+++ b/src/curveeditor/detail/keyframeitem.h
@@ -27,6 +27,7 @@
#include "curveeditorstyle.h"
#include "keyframe.h"
+#include "selectableitem.h"
#include <QGraphicsObject>
@@ -34,12 +35,18 @@ namespace DesignTools {
class HandleItem;
-class KeyframeItem : public QGraphicsObject
+enum class HandleSlot { Undefined, Left, Right };
+
+class KeyframeItem : public SelectableItem
{
Q_OBJECT
signals:
- void keyframeChanged();
+ void redrawCurve();
+
+ void keyframeMoved(KeyframeItem *item, const QPointF &direction);
+
+ void handleMoved(KeyframeItem *frame, HandleSlot handle, double angle, double deltaLength);
public:
KeyframeItem(QGraphicsItem *parent = nullptr);
@@ -48,13 +55,13 @@ public:
~KeyframeItem() override;
- enum { Type = UserType + 2 };
+ enum { Type = ItemTypeKeyframe };
int type() const override;
QRectF boundingRect() const override;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
Keyframe keyframe() const;
@@ -62,11 +69,17 @@ public:
void setStyle(const CurveEditorStyle &style);
+ void moveKeyframe(const QPointF &direction);
+
+ void moveHandle(HandleSlot handle, double deltaAngle, double deltaLength);
+
protected:
QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
+ void selectionCallback() override;
+
private:
- void updatePosition();
+ void updatePosition(bool emit = true);
void updateHandle(HandleItem *handle, bool emit = true);
diff --git a/src/curveeditor/detail/playhead.cpp b/src/curveeditor/detail/playhead.cpp
index 15edb4b..e2e6d21 100644
--- a/src/curveeditor/detail/playhead.cpp
+++ b/src/curveeditor/detail/playhead.cpp
@@ -68,8 +68,11 @@ void Playhead::resize(GraphicsView *view)
CurveEditorStyle style = view->editorStyle();
- QPointF tl = viewRect.topLeft() + QPointF(style.valueAxisWidth, style.timeAxisHeight - style.playhead.width);
- QPointF br = viewRect.bottomLeft() + QPointF(style.valueAxisWidth + style.playhead.width, -g_playheadMargin);
+ QPointF tlr(style.valueAxisWidth, style.timeAxisHeight - style.playhead.width);
+ QPointF brr(style.valueAxisWidth + style.playhead.width, -g_playheadMargin);
+
+ QPointF tl = viewRect.topLeft() + tlr;
+ QPointF br = viewRect.bottomLeft() + brr;
m_rect = QRectF(tl, br);
@@ -108,45 +111,29 @@ void Playhead::mouseMoveOutOfBounds(GraphicsView *view)
return;
CurveEditorStyle style = view->editorStyle();
-
QRectF canvas = view->canvasRect();
-
QPointF pos = view->globalToScene(QCursor::pos());
if (pos.x() > canvas.right()) {
-
double speed = (pos.x() - canvas.right());
-
double nextCenter = m_rect.center().x() + speed;
-
double frame = std::round(view->mapXtoTime(nextCenter));
-
- double framePosition = view->mapTimeToX(frame);
-
view->setCurrentFrame(frame);
+ double framePosition = view->mapTimeToX(frame);
double rightSideOut = framePosition + style.playhead.width / 2.0 + style.canvasMargin;
-
double overshoot = rightSideOut - canvas.right();
-
view->scrollContent(overshoot, 0.0);
} else if (pos.x() < canvas.left()) {
-
double speed = (canvas.left() - pos.x());
-
double nextCenter = m_rect.center().x() - speed;
-
double frame = std::round(view->mapXtoTime(nextCenter));
-
- double framePosition = view->mapTimeToX(frame);
-
view->setCurrentFrame(frame);
+ double framePosition = view->mapTimeToX(frame);
double leftSideOut = framePosition - style.playhead.width / 2.0 - style.canvasMargin;
-
double undershoot = canvas.left() - leftSideOut;
-
view->scrollContent(-undershoot, 0.0);
}
@@ -192,7 +179,6 @@ void Playhead::paint(QPainter *painter, GraphicsView *view) const
path.closeSubpath();
painter->fillPath(path, style.playhead.color);
-
painter->drawLine(top + QPointF(0., 5.), QPointF(m_rect.center().x(), m_rect.bottom()));
painter->restore();
diff --git a/src/curveeditor/detail/selectableitem.cpp b/src/curveeditor/detail/selectableitem.cpp
new file mode 100644
index 0000000..fb61682
--- /dev/null
+++ b/src/curveeditor/detail/selectableitem.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "selectableitem.h"
+#include "keyframeitem.h"
+
+#include <QDebug>
+
+namespace DesignTools {
+
+SelectableItem::SelectableItem(QGraphicsItem *parent)
+ : QGraphicsObject(parent)
+ , m_active(false)
+ , m_selected(false)
+ , m_preSelected(SelectionMode::Undefined)
+{
+ setFlag(QGraphicsItem::ItemIsSelectable, false);
+
+ setFlag(QGraphicsItem::ItemIsMovable, true);
+ setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
+ setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
+}
+
+SelectableItem::~SelectableItem() {}
+
+bool SelectableItem::activated() const
+{
+ return m_active;
+}
+
+bool SelectableItem::selected() const
+{
+ switch (m_preSelected) {
+ case SelectionMode::Clear:
+ return false;
+ case SelectionMode::New:
+ return true;
+ case SelectionMode::Add:
+ return true;
+ case SelectionMode::Remove:
+ return false;
+ case SelectionMode::Toggle:
+ return !m_selected;
+ default:
+ return m_selected;
+ }
+
+ return false;
+}
+
+void SelectableItem::setActivated(bool active)
+{
+ m_active = active;
+}
+
+void SelectableItem::setPreselected(SelectionMode mode)
+{
+ m_preSelected = mode;
+ selectionCallback();
+}
+
+void SelectableItem::applyPreselection()
+{
+ m_selected = selected();
+ m_preSelected = SelectionMode::Undefined;
+}
+
+void SelectableItem::selectionCallback() {}
+
+void SelectableItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ m_active = true;
+ QGraphicsObject::mousePressEvent(event);
+}
+
+void SelectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (type() == KeyframeItem::Type && !selected())
+ return;
+
+ QGraphicsObject::mouseMoveEvent(event);
+}
+
+void SelectableItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ m_active = false;
+ QGraphicsObject::mouseReleaseEvent(event);
+}
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/selectableitem.h b/src/curveeditor/detail/selectableitem.h
new file mode 100644
index 0000000..0a2a489
--- /dev/null
+++ b/src/curveeditor/detail/selectableitem.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QGraphicsObject>
+
+namespace DesignTools {
+
+enum ItemType
+{
+ ItemTypeKeyframe = QGraphicsItem::UserType + 1,
+ ItemTypeHandle = QGraphicsItem::UserType + 2,
+ ItemTypeCurve = QGraphicsItem::UserType + 3
+};
+
+enum class SelectionMode : unsigned int
+{
+ Undefined,
+ Clear,
+ New,
+ Add,
+ Remove,
+ Toggle
+};
+
+class SelectableItem : public QGraphicsObject
+{
+ Q_OBJECT
+
+public:
+ SelectableItem(QGraphicsItem *parent = nullptr);
+
+ ~SelectableItem() override;
+
+ bool activated() const;
+
+ bool selected() const;
+
+ void setActivated(bool active);
+
+ void setPreselected(SelectionMode mode);
+
+ void applyPreselection();
+
+protected:
+ virtual void selectionCallback();
+
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+
+private:
+ bool m_active;
+
+ bool m_selected;
+
+ SelectionMode m_preSelected;
+};
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/selector.cpp b/src/curveeditor/detail/selector.cpp
new file mode 100644
index 0000000..b6e7a8f
--- /dev/null
+++ b/src/curveeditor/detail/selector.cpp
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#include "selector.h"
+#include "graphicsview.h"
+#include "keyframeitem.h"
+#include "playhead.h"
+
+#include <QApplication>
+
+namespace DesignTools {
+
+Selector::Selector() {}
+
+void Selector::paint(QPainter *painter)
+{
+ QPen pen(Qt::white);
+
+ painter->save();
+ painter->setPen(pen);
+
+ if (!m_lasso.isEmpty())
+ painter->drawPath(m_lasso);
+
+ if (!m_rect.isNull())
+ painter->drawRect(m_rect);
+
+ painter->restore();
+}
+
+void Selector::mousePress(QMouseEvent *event, GraphicsView *view)
+{
+ m_shortcut = Shortcut(event);
+
+ if (view->hasActiveHandle())
+ return;
+
+ if (select(SelectionTool::Undefined, view->globalToScene(event->globalPos()), view))
+ applyPreSelection(view);
+
+ m_mouseInit = event->globalPos();
+ m_mouseCurr = event->globalPos();
+
+ QPointF click = view->globalToScene(m_mouseInit);
+
+ m_lasso = QPainterPath(click);
+ m_lasso.closeSubpath();
+
+ m_rect = QRectF(click, click);
+}
+
+void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead)
+{
+ if (m_mouseInit.isNull())
+ return;
+
+ QPointF delta = event->globalPos() - m_mouseInit;
+ if (delta.manhattanLength() < QApplication::startDragDistance())
+ return;
+
+ if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
+ || m_shortcut == m_shortcuts.removeFromSelection
+ || m_shortcut == m_shortcuts.toggleSelection) {
+ if (view->hasActiveItem())
+ return;
+
+ select(m_tool, view->globalToScene(event->globalPos()), view);
+
+ event->accept();
+ view->viewport()->update();
+
+ } else if (m_shortcut == m_shortcuts.zoom) {
+ double bigger = std::abs(delta.x()) > std::abs(delta.y()) ? delta.x() : delta.y();
+ double factor = bigger / view->width();
+ view->setZoomX(view->zoomX() + factor, m_mouseInit);
+ m_mouseCurr = event->globalPos();
+ event->accept();
+
+ } else if (m_shortcut == m_shortcuts.pan) {
+ view->scrollContent(-delta.x(), -delta.y());
+ playhead.resize(view);
+ m_mouseCurr = event->globalPos();
+ }
+}
+
+void Selector::mouseRelease(QMouseEvent *event, GraphicsView *view)
+{
+ Q_UNUSED(event);
+
+ applyPreSelection(view);
+
+ m_shortcut = Shortcut();
+ m_mouseInit = QPoint();
+ m_mouseCurr = QPoint();
+ m_lasso = QPainterPath();
+ m_rect = QRectF();
+}
+
+bool Selector::select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view)
+{
+ auto selectWidthTool = [this, tool](SelectionMode mode, const QPointF &pos, GraphicsView *view) {
+ switch (tool) {
+ case SelectionTool::Lasso:
+ return lassoSelection(mode, pos, view);
+ case SelectionTool::Rectangle:
+ return rectangleSelection(mode, pos, view);
+ default:
+ return pressSelection(mode, pos, view);
+ }
+ };
+
+ if (m_shortcut == m_shortcuts.newSelection) {
+ clearSelection(view);
+ return selectWidthTool(SelectionMode::New, pos, view);
+ } else if (m_shortcut == m_shortcuts.addToSelection) {
+ return selectWidthTool(SelectionMode::Add, pos, view);
+ } else if (m_shortcut == m_shortcuts.removeFromSelection) {
+ return selectWidthTool(SelectionMode::Remove, pos, view);
+ } else if (m_shortcut == m_shortcuts.toggleSelection) {
+ return selectWidthTool(SelectionMode::Toggle, pos, view);
+ }
+
+ return false;
+}
+
+bool Selector::pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *frame = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ QRectF itemRect = frame->mapRectToScene(frame->boundingRect());
+ if (itemRect.contains(pos)) {
+ frame->setPreselected(mode);
+ out = true;
+ }
+ }
+ }
+ return out;
+}
+
+bool Selector::rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ m_rect.setBottomRight(pos);
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (m_rect.contains(keyframeItem->pos())) {
+ keyframeItem->setPreselected(mode);
+ out = true;
+ } else {
+ keyframeItem->setPreselected(SelectionMode::Undefined);
+ }
+ }
+ }
+ return out;
+}
+
+bool Selector::lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
+{
+ bool out = false;
+ m_lasso.lineTo(pos);
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ if (m_lasso.contains(keyframeItem->pos())) {
+ keyframeItem->setPreselected(mode);
+ out = true;
+ } else {
+ keyframeItem->setPreselected(SelectionMode::Undefined);
+ }
+ }
+ }
+ return out;
+}
+
+void Selector::clearSelection(GraphicsView *view)
+{
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
+ frameItem->setPreselected(SelectionMode::Clear);
+ frameItem->applyPreselection();
+ }
+ }
+}
+
+void Selector::applyPreSelection(GraphicsView *view)
+{
+ const auto itemList = view->items();
+ for (auto *item : itemList) {
+ if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item))
+ keyframeItem->applyPreselection();
+ }
+}
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/selector.h b/src/curveeditor/detail/selector.h
new file mode 100644
index 0000000..e8f53d9
--- /dev/null
+++ b/src/curveeditor/detail/selector.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "curveeditorstyle.h"
+#include "selectableitem.h"
+
+#include <QMouseEvent>
+#include <QPainterPath>
+#include <QPoint>
+#include <QRectF>
+
+namespace DesignTools {
+
+class GraphicsView;
+class Playhead;
+
+enum class SelectionTool { Undefined, Lasso, Rectangle };
+
+class Selector
+{
+public:
+ Selector();
+
+ void paint(QPainter *painter);
+
+ void mousePress(QMouseEvent *event, GraphicsView *view);
+
+ void mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead);
+
+ void mouseRelease(QMouseEvent *event, GraphicsView *view);
+
+private:
+ bool select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view);
+
+ bool pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ bool rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ bool lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
+
+ void clearSelection(GraphicsView *view);
+
+ void applyPreSelection(GraphicsView *view);
+
+ Shortcuts m_shortcuts = Shortcuts();
+
+ Shortcut m_shortcut;
+
+ SelectionMode m_mode = SelectionMode::Undefined;
+
+ SelectionTool m_tool = SelectionTool::Rectangle;
+
+ QPoint m_mouseInit = QPoint();
+
+ QPoint m_mouseCurr = QPoint();
+
+ QPainterPath m_lasso = QPainterPath();
+
+ QRectF m_rect = QRectF();
+};
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/shortcut.cpp b/src/curveeditor/detail/shortcut.cpp
new file mode 100644
index 0000000..d6d04db
--- /dev/null
+++ b/src/curveeditor/detail/shortcut.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+#include "shortcut.h"
+
+namespace DesignTools {
+
+Shortcut::Shortcut()
+ : m_key()
+ , m_buttons()
+ , m_modifiers()
+{}
+
+Shortcut::Shortcut(QMouseEvent *event)
+ : m_key()
+ , m_buttons(event->buttons())
+ , m_modifiers(event->modifiers())
+{}
+
+Shortcut::Shortcut(const Qt::KeyboardModifiers &mods, const Qt::Key &key)
+ : m_key(key)
+ , m_buttons()
+ , m_modifiers(mods)
+{}
+
+Shortcut::Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods)
+ : m_key()
+ , m_buttons(buttons)
+ , m_modifiers(mods)
+{}
+
+Shortcut::Shortcut(const Qt::MouseButtons &buttons,
+ const Qt::KeyboardModifiers &mods,
+ const Qt::Key &key)
+ : m_key(key)
+ , m_buttons(buttons)
+ , m_modifiers(mods)
+{}
+
+bool Shortcut::exactMatch(const Qt::Key &key) const
+{
+ return m_key == key;
+}
+
+bool Shortcut::exactMatch(const Qt::MouseButton &button) const
+{
+ return static_cast<int>(m_buttons) == static_cast<int>(button);
+}
+
+bool Shortcut::exactMatch(const Qt::KeyboardModifier &modifier) const
+{
+ return static_cast<int>(m_modifiers) == static_cast<int>(modifier);
+}
+
+bool Shortcut::operator==(const Shortcut &other) const
+{
+ return m_key == other.m_key && m_buttons == other.m_buttons && m_modifiers == other.m_modifiers;
+}
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/shortcut.h b/src/curveeditor/detail/shortcut.h
new file mode 100644
index 0000000..a9e075b
--- /dev/null
+++ b/src/curveeditor/detail/shortcut.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Design Tooling
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QMouseEvent>
+
+namespace DesignTools {
+
+class Shortcut
+{
+public:
+ Shortcut();
+
+ Shortcut(QMouseEvent *event);
+
+ Shortcut(const Qt::KeyboardModifiers &mods, const Qt::Key &key);
+
+ Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods = Qt::NoModifier);
+
+ Shortcut(const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &mods, const Qt::Key &key);
+
+ bool exactMatch(const Qt::Key &key) const;
+
+ bool exactMatch(const Qt::MouseButton &button) const;
+
+ bool exactMatch(const Qt::KeyboardModifier &modifier) const;
+
+ bool operator==(const Shortcut &other) const;
+
+private:
+ Qt::Key m_key;
+
+ Qt::MouseButtons m_buttons;
+
+ Qt::KeyboardModifiers m_modifiers;
+};
+
+} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/utils.h b/src/curveeditor/detail/utils.h
index 450c805..812c5d0 100644
--- a/src/curveeditor/detail/utils.h
+++ b/src/curveeditor/detail/utils.h
@@ -33,8 +33,6 @@ class QTransform;
namespace DesignTools {
-class Keyframe;
-
double clamp(double val, double lo, double hi);
double lerp(double blend, double a, double b);