diff options
author | Knud Dollereder <knud.dollereder@qt.io> | 2019-05-24 15:03:51 +0200 |
---|---|---|
committer | Knud Dollereder <knud.dollereder@qt.io> | 2019-05-29 14:42:32 +0000 |
commit | 69e0ce6b1aee59585f141a320cef4f980e6ae7dc (patch) | |
tree | 4dcd6b41099fb621dc205cc0ee28cedc91672fbc | |
parent | df6afb91041d5225448a13aaf3bbb6f11137b0c0 (diff) |
Implement selection of keyframes and handles
Change-Id: Iaedb5561f9d57e38bfcac6fa5012a26ac5033d3b
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
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); |