Refactor GraphicsScene and Graphicsview

Make sure to deselect keyframes when clicking on an unselected handle

Task-number: QDS-1033
Change-Id: I2b98188237f8a0618e6ff3e64280091583112196
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Knud Dollereder
2020-04-06 11:22:30 +02:00
parent 93e788fd19
commit 7e76a76402
16 changed files with 520 additions and 306 deletions

View File

@@ -73,6 +73,8 @@ struct CurveItemStyleOption
QColor color = QColor(0, 200, 0);
QColor selectionColor = QColor(200, 200, 200);
QColor easingCurveColor = QColor(200, 0, 200);
QColor lockedColor = QColor(50, 50, 50);
QColor hoverColor = QColor(200, 0, 200);
};
struct PlayheadStyleOption

View File

@@ -118,10 +118,10 @@ void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidg
pen.setColor(m_style.easingCurveColor);
} else {
if (locked())
pen.setColor(Qt::black);
pen.setColor(m_style.lockedColor);
else if (isUnderMouse())
pen.setColor(Qt::red);
else if (hasSelection())
pen.setColor(m_style.hoverColor);
else if (hasSelectedKeyframe())
pen.setColor(m_style.selectionColor);
else
pen.setColor(m_style.color);
@@ -146,7 +146,25 @@ bool CurveItem::isDirty() const
return m_itemDirty;
}
bool CurveItem::hasSelection() const
bool CurveItem::hasActiveKeyframe() const
{
for (auto *frame : m_keyframes) {
if (frame->activated())
return true;
}
return false;
}
bool CurveItem::hasActiveHandle() const
{
for (auto *frame : m_keyframes) {
if (frame->hasActiveHandle())
return true;
}
return false;
}
bool CurveItem::hasSelectedKeyframe() const
{
for (auto *frame : m_keyframes) {
if (frame->selected())
@@ -203,7 +221,7 @@ std::vector<AnimationCurve> CurveItem::curves() const
std::vector<Keyframe> tmp;
for (size_t i = 0; i < m_keyframes.size(); ++i) {
for (int i = 0; i < m_keyframes.size(); ++i) {
KeyframeItem *item = m_keyframes[i];
Keyframe current = item->keyframe();
@@ -233,6 +251,33 @@ std::vector<AnimationCurve> CurveItem::curves() const
return out;
}
QVector<KeyframeItem *> CurveItem::keyframes() const
{
return m_keyframes;
}
QVector<KeyframeItem *> CurveItem::selectedKeyframes() const
{
QVector<KeyframeItem *> out;
for (auto *frame : m_keyframes) {
if (frame->selected())
out.push_back(frame);
}
return out;
}
QVector<HandleItem *> CurveItem::handles() const
{
QVector<HandleItem *> out;
for (auto *frame : m_keyframes) {
if (auto *left = frame->leftHandle())
out.push_back(left);
if (auto *right = frame->rightHandle())
out.push_back(right);
}
return out;
}
void CurveItem::restore()
{
if (m_keyframes.empty())
@@ -248,7 +293,7 @@ void CurveItem::restore()
if (prevItem->hasLeftHandle())
prevItem->setLeftHandle(QPointF());
for (size_t i = 1; i < m_keyframes.size(); ++i) {
for (int i = 1; i < m_keyframes.size(); ++i) {
KeyframeItem *currItem = m_keyframes[i];
bool left = prevItem->hasRightHandle();
@@ -328,7 +373,7 @@ void CurveItem::setInterpolation(Keyframe::Interpolation interpolation)
return;
KeyframeItem *prevItem = m_keyframes[0];
for (size_t i = 1; i < m_keyframes.size(); ++i) {
for (int i = 1; i < m_keyframes.size(); ++i) {
KeyframeItem *currItem = m_keyframes[i];
if (currItem->selected()) {
Keyframe prev = prevItem->keyframe();

View File

@@ -73,7 +73,11 @@ public:
bool isDirty() const;
bool hasSelection() const;
bool hasActiveKeyframe() const;
bool hasActiveHandle() const;
bool hasSelectedKeyframe() const;
unsigned int id() const;
@@ -87,6 +91,12 @@ public:
std::vector<AnimationCurve> curves() const;
QVector<KeyframeItem *> keyframes() const;
QVector<KeyframeItem *> selectedKeyframes() const;
QVector<HandleItem *> handles() const;
void restore();
void setDirty(bool dirty);
@@ -126,7 +136,7 @@ private:
QTransform m_transform;
std::vector<KeyframeItem *> m_keyframes;
QVector<KeyframeItem *> m_keyframes;
bool m_itemDirty;
};

View File

@@ -31,20 +31,60 @@
#include <QGraphicsSceneMouseEvent>
#include <cmath>
namespace DesignTools {
GraphicsScene::GraphicsScene(QObject *parent)
: QGraphicsScene(parent)
, m_curves()
, m_dirty(true)
, m_limits()
, m_doNotMoveItems(false)
{}
GraphicsScene::~GraphicsScene()
{
m_curves.clear();
}
bool GraphicsScene::empty() const
{
return items().empty();
}
bool GraphicsScene::hasActiveKeyframe() const
{
for (auto *curve : m_curves) {
if (curve->hasActiveKeyframe())
return true;
}
return false;
}
bool GraphicsScene::hasActiveHandle() const
{
for (auto *curve : m_curves) {
if (curve->hasActiveHandle())
return true;
}
return false;
}
bool GraphicsScene::hasActiveItem() const
{
return hasActiveKeyframe() || hasActiveHandle();
}
bool GraphicsScene::hasSelectedKeyframe() const
{
for (auto *curve : m_curves) {
if (curve->hasSelectedKeyframe())
return true;
}
return false;
}
double GraphicsScene::minimumTime() const
{
return limits().left();
@@ -65,6 +105,103 @@ double GraphicsScene::maximumValue() const
return limits().top();
}
QRectF GraphicsScene::rect() const
{
return sceneRect();
}
QVector<CurveItem *> GraphicsScene::curves() const
{
return m_curves;
}
QVector<CurveItem *> GraphicsScene::selectedCurves() const
{
QVector<CurveItem *> out;
for (auto *curve : m_curves) {
if (curve->hasSelectedKeyframe())
out.push_back(curve);
}
return out;
}
QVector<KeyframeItem *> GraphicsScene::keyframes() const
{
QVector<KeyframeItem *> out;
for (auto *curve : m_curves)
out.append(curve->keyframes());
return out;
}
QVector<KeyframeItem *> GraphicsScene::selectedKeyframes() const
{
QVector<KeyframeItem *> out;
for (auto *curve : m_curves)
out.append(curve->selectedKeyframes());
return out;
}
CurveItem *GraphicsScene::findCurve(unsigned int id) const
{
for (auto *curve : m_curves) {
if (curve->id() == id)
return curve;
}
return nullptr;
}
SelectableItem *GraphicsScene::intersect(const QPointF &pos) const
{
auto hitTest = [pos](QGraphicsObject *item) {
return item->mapRectToScene(item->boundingRect()).contains(pos);
};
const auto frames = keyframes();
for (auto *frame : frames) {
if (hitTest(frame))
return frame;
if (auto *leftHandle = frame->leftHandle()) {
if (hitTest(leftHandle))
return leftHandle;
}
if (auto *rightHandle = frame->rightHandle()) {
if (hitTest(rightHandle))
return rightHandle;
}
}
return nullptr;
}
void GraphicsScene::reset()
{
m_curves.clear();
clear();
}
void GraphicsScene::deleteSelectedKeyframes()
{
for (auto *curve : m_curves)
curve->deleteSelectedKeyframes();
}
void GraphicsScene::insertKeyframe(double time, bool all)
{
if (!all) {
for (auto *curve : m_curves) {
if (curve->isUnderMouse())
curve->insertKeyframeByTime(std::round(time));
}
return;
}
for (auto *curve : m_curves)
curve->insertKeyframeByTime(std::round(time));
}
void GraphicsScene::doNotMoveItems(bool val)
{
m_doNotMoveItems = val;
@@ -76,16 +213,15 @@ void GraphicsScene::addCurveItem(CurveItem *item)
item->setDirty(false);
item->connect(this);
addItem(item);
m_curves.push_back(item);
}
void GraphicsScene::setComponentTransform(const QTransform &transform)
{
QRectF bounds;
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
bounds = bounds.united(curveItem->setComponentTransform(transform));
}
for (auto *curve : m_curves)
bounds = bounds.united(curve->setComponentTransform(transform));
if (bounds.isNull()) {
if (GraphicsView *gview = graphicsView())
@@ -98,29 +234,20 @@ void GraphicsScene::setComponentTransform(const QTransform &transform)
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);
for (auto *curve : m_curves) {
for (auto *keyframe : curve->keyframes()) {
if (keyframe != movedItem && keyframe->selected())
keyframe->moveKeyframe(direction);
}
}
}
void GraphicsScene::handleUnderMouse(HandleItem *handle)
{
const auto itemList = items();
for (auto *item : itemList) {
if (item == handle)
continue;
if (auto *keyItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
if (keyItem->selected()) {
keyItem->setActivated(handle->isUnderMouse(), handle->slot());
}
for (auto *curve : m_curves) {
for (auto *keyframe : curve->keyframes()) {
if (keyframe->selected())
keyframe->setActivated(handle->isUnderMouse(), handle->slot());
}
}
}
@@ -142,16 +269,13 @@ void GraphicsScene::handleMoved(KeyframeItem *frame,
}
};
const auto itemList = items();
for (auto *item : itemList) {
if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
if (item == frame) {
moveUnified(frameItem);
continue;
}
if (frameItem->selected()) {
frameItem->moveHandle(handle, angle, deltaLength);
moveUnified(frameItem);
for (auto *curve : m_curves) {
for (auto *keyframe : curve->keyframes()) {
if (keyframe == frame)
moveUnified(keyframe);
else if (keyframe->selected()) {
keyframe->moveHandle(handle, angle, deltaLength);
moveUnified(keyframe);
}
}
}
@@ -159,27 +283,24 @@ void GraphicsScene::handleMoved(KeyframeItem *frame,
void GraphicsScene::setPinned(uint id, bool pinned)
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
if (curveItem->id() == id)
curveItem->setPinned(pinned);
}
}
if (CurveItem *curve = findCurve(id))
curve->setPinned(pinned);
}
std::vector<CurveItem *> GraphicsScene::takePinnedItems()
{
std::vector<CurveItem *> out;
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
if (curveItem->pinned()) {
removeItem(curveItem);
out.push_back(curveItem);
}
for (auto *curve : m_curves) {
if (curve->pinned())
out.push_back(curve);
}
for (auto *curve : out) {
curve->disconnect(this);
m_curves.removeOne(curve);
removeItem(curve);
}
return out;
}
@@ -187,12 +308,24 @@ void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
const auto itemList = items();
for (auto *item : itemList) {
if (auto *handleItem = qgraphicsitem_cast<HandleItem *>(item))
handleItem->setIsUnderMouse(handleItem->contains(mouseEvent->scenePos()));
else if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
curveItem->setIsUnderMouse(curveItem->contains(mouseEvent->scenePos()));
QPointF mouse = mouseEvent->scenePos();
bool hasHandle = false;
for (auto *curve : m_curves) {
for (auto *handle : curve->handles()) {
bool intersects = handle->contains(mouse);
handle->setIsUnderMouse(intersects);
if (intersects)
hasHandle = true;
}
}
if (hasHandle) {
for (auto *curve : m_curves)
curve->setIsUnderMouse(false);
} else {
for (auto *curve : m_curves)
curve->setIsUnderMouse(curve->contains(mouseEvent->scenePos()));
}
}
@@ -200,17 +333,13 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseReleaseEvent(mouseEvent);
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
for (auto *curve : m_curves) {
// CurveItems might become invalid after a keyframe-drag operation.
curveItem->restore();
if (curveItem->isDirty()) {
curve->restore();
if (curve->isDirty()) {
m_dirty = true;
curveItem->setDirty(false);
emit curveChanged(curveItem->id(), curveItem->curve());
}
curve->setDirty(false);
emit curveChanged(curve->id(), curve->curve());
}
}
@@ -218,40 +347,11 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
graphicsView()->setZoomY(0.0);
}
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();
for (auto &&view : viewList) {
if (GraphicsView *gview = qobject_cast<GraphicsView *>(view))
if (viewList.size() == 1) {
if (GraphicsView *gview = qobject_cast<GraphicsView *>(viewList.at(0)))
return gview;
}
return nullptr;
@@ -263,9 +363,7 @@ QRectF GraphicsScene::limits() const
QPointF min(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
QPointF max(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest());
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
for (auto *curveItem : m_curves) {
auto curve = curveItem->resolvedCurve();
if (min.x() > curve.minimumTime())
min.rx() = curve.minimumTime();
@@ -279,7 +377,6 @@ QRectF GraphicsScene::limits() const
if (max.y() < curve.maximumValue())
max.ry() = curve.maximumValue();
}
}
m_limits = QRectF(QPointF(min.x(), max.y()), QPointF(max.x(), min.y()));
m_dirty = false;

View File

@@ -45,6 +45,8 @@ signals:
public:
GraphicsScene(QObject *parent = nullptr);
~GraphicsScene() override;
bool empty() const;
bool hasActiveKeyframe() const;
@@ -53,6 +55,8 @@ public:
bool hasActiveItem() const;
bool hasSelectedKeyframe() const;
double minimumTime() const;
double maximumTime() const;
@@ -61,6 +65,28 @@ public:
double maximumValue() const;
QRectF rect() const;
QVector<CurveItem *> curves() const;
QVector<CurveItem *> selectedCurves() const;
QVector<KeyframeItem *> keyframes() const;
QVector<KeyframeItem *> selectedKeyframes() const;
QVector<HandleItem *> handles() const;
CurveItem *findCurve(unsigned int id) const;
SelectableItem *intersect(const QPointF &pos) const;
void reset();
void deleteSelectedKeyframes();
void insertKeyframe(double time, bool all = false);
void doNotMoveItems(bool tmp);
void addCurveItem(CurveItem *item);
@@ -85,10 +111,16 @@ protected:
private:
using QGraphicsScene::addItem;
using QGraphicsScene::clear;
using QGraphicsScene::removeItem;
GraphicsView *graphicsView() const;
QRectF limits() const;
QVector<CurveItem *> m_curves;
mutable bool m_dirty;
mutable QRectF m_limits;

View File

@@ -43,7 +43,7 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
, m_zoomX(0.0)
, m_zoomY(0.0)
, m_transform()
, m_scene()
, m_scene(new GraphicsScene())
, m_model(model)
, m_playhead(this)
, m_selector()
@@ -52,7 +52,7 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
{
model->setGraphicsView(this);
setScene(&m_scene);
setScene(m_scene);
setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
setResizeAnchor(QGraphicsView::NoAnchor);
setRenderHint(QPainter::Antialiasing, true);
@@ -68,15 +68,23 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
m_model->setCurve(id, curve);
};
connect(&m_scene, &GraphicsScene::curveChanged, itemSlot);
connect(m_scene, &GraphicsScene::curveChanged, itemSlot);
auto pinSlot = [this](PropertyTreeItem *pti) { m_scene.setPinned(pti->id(), pti->pinned()); };
auto pinSlot = [this](PropertyTreeItem *pti) { m_scene->setPinned(pti->id(), pti->pinned()); };
connect(m_model, &CurveEditorModel::curveChanged, pinSlot);
applyZoom(m_zoomX, m_zoomY);
update();
}
GraphicsView::~GraphicsView()
{
if (m_scene) {
delete m_scene;
m_scene = nullptr;
}
}
CurveEditorModel *GraphicsView::model() const
{
return m_model;
@@ -87,48 +95,26 @@ CurveEditorStyle GraphicsView::editorStyle() const
return m_style;
}
bool GraphicsView::hasActiveItem() const
{
return m_scene.hasActiveItem();
}
bool GraphicsView::hasActiveHandle() const
{
return m_scene.hasActiveHandle();
}
bool GraphicsView::hasSelectedKeyframe() const
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
if (curveItem->hasSelection())
return true;
}
}
return false;
}
double GraphicsView::minimumTime() const
{
bool check = m_model->minimumTime() < m_scene.minimumTime();
return check ? m_model->minimumTime() : m_scene.minimumTime();
bool check = m_model->minimumTime() < m_scene->minimumTime();
return check ? m_model->minimumTime() : m_scene->minimumTime();
}
double GraphicsView::maximumTime() const
{
bool check = m_model->maximumTime() > m_scene.maximumTime();
return check ? m_model->maximumTime() : m_scene.maximumTime();
bool check = m_model->maximumTime() > m_scene->maximumTime();
return check ? m_model->maximumTime() : m_scene->maximumTime();
}
double GraphicsView::minimumValue() const
{
return m_scene.empty() ? -1.0 : m_scene.minimumValue();
return m_scene->empty() ? -1.0 : m_scene->minimumValue();
}
double GraphicsView::maximumValue() const
{
return m_scene.empty() ? 1.0 : m_scene.maximumValue();
return m_scene->empty() ? 1.0 : m_scene->maximumValue();
}
double GraphicsView::zoomX() const
@@ -179,11 +165,9 @@ void GraphicsView::setStyle(const CurveEditorStyle &style)
{
m_style = style;
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
curveItem->setStyle(style);
}
const auto curves = m_scene->curves();
for (auto *curve : curves)
curve->setStyle(style);
applyZoom(m_zoomX, m_zoomY);
viewport()->update();
@@ -191,12 +175,8 @@ void GraphicsView::setStyle(const CurveEditorStyle &style)
void GraphicsView::setLocked(PropertyTreeItem *item)
{
const auto itemList = items();
for (auto *gitem : itemList) {
if (auto *citem = qgraphicsitem_cast<CurveItem *>(gitem))
if (item->id() == citem->id())
citem->setLocked(item->locked());
}
if (CurveItem *curve = m_scene->findCurve(item->id()))
curve->setLocked(item->locked());
}
void GraphicsView::setZoomX(double zoom, const QPoint &pivot)
@@ -231,9 +211,9 @@ void GraphicsView::scrollContent(double x, double y)
void GraphicsView::reset(const std::vector<CurveItem *> &items)
{
m_scene.clear();
m_scene->reset();
for (auto *item : items)
m_scene.addCurveItem(item);
m_scene->addCurveItem(item);
applyZoom(m_zoomX, m_zoomY);
viewport()->update();
@@ -241,49 +221,31 @@ void GraphicsView::reset(const std::vector<CurveItem *> &items)
void GraphicsView::updateSelection(const std::vector<CurveItem *> &items)
{
const std::vector<CurveItem *> pinnedItems = m_scene.takePinnedItems();
auto notPinned = [pinnedItems](CurveItem *item) {
for (auto *pinned : pinnedItems) {
if (pinned->id() == item->id())
return false;
std::vector<CurveItem *> preservedItems = m_scene->takePinnedItems();
for (auto *curve : items) {
auto finder = [curve](CurveItem *item) { return curve->id() == item->id(); };
auto iter = std::find_if(preservedItems.begin(), preservedItems.end(), finder);
if (iter == preservedItems.end())
preservedItems.push_back(curve);
}
return true;
};
m_scene.clear();
for (auto *item : items) {
if (notPinned(item))
m_scene.addCurveItem(item);
}
for (auto *item : pinnedItems)
m_scene.addCurveItem(item);
applyZoom(m_zoomX, m_zoomY);
viewport()->update();
reset(preservedItems);
}
void GraphicsView::setInterpolation(Keyframe::Interpolation interpol)
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *citem = qgraphicsitem_cast<CurveItem *>(item))
if (citem->hasSelection())
citem->setInterpolation(interpol);
}
const auto selectedCurves = m_scene->selectedCurves();
for (auto *curve : selectedCurves)
curve->setInterpolation(interpol);
viewport()->update();
}
void GraphicsView::toggleUnified()
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *citem = qgraphicsitem_cast<CurveItem *>(item)) {
if (citem->hasSelection())
citem->toggleUnified();
}
}
const auto selectedCurves = m_scene->selectedCurves();
for (auto *curve : selectedCurves)
curve->toggleUnified();
viewport()->update();
}
@@ -299,7 +261,7 @@ void GraphicsView::keyPressEvent(QKeyEvent *event)
if (shortcut == m_style.shortcuts.frameAll)
applyZoom(0.0, 0.0);
else if (shortcut == m_style.shortcuts.deleteKeyframe)
deleteSelectedKeyframes();
m_scene->deleteSelectedKeyframes();
}
void GraphicsView::mousePressEvent(QMouseEvent *event)
@@ -309,7 +271,7 @@ void GraphicsView::mousePressEvent(QMouseEvent *event)
Shortcut shortcut(event);
if (shortcut == m_style.shortcuts.insertKeyframe) {
insertKeyframe(globalToRaster(event->globalPos()).x());
m_scene->insertKeyframe(globalToRaster(event->globalPos()).x());
return;
}
@@ -325,7 +287,7 @@ void GraphicsView::mousePressEvent(QMouseEvent *event)
QGraphicsView::mousePressEvent(event);
m_selector.mousePress(event, this);
m_selector.mousePress(event, this, m_scene);
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
@@ -335,7 +297,7 @@ void GraphicsView::mouseMoveEvent(QMouseEvent *event)
QGraphicsView::mouseMoveEvent(event);
m_selector.mouseMove(event, this, m_playhead);
m_selector.mouseMove(event, this, m_scene, m_playhead);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
@@ -343,7 +305,7 @@ void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
QGraphicsView::mouseReleaseEvent(event);
m_playhead.mouseRelease(this);
m_selector.mouseRelease(event, this);
m_selector.mouseRelease(event, m_scene);
this->viewport()->update();
}
@@ -371,16 +333,16 @@ void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
menu.addSeparator();
auto insertKeyframes = [this, event]() {
insertKeyframe(globalToRaster(event->globalPos()).x(), true);
m_scene->insertKeyframe(globalToRaster(event->globalPos()).x(), true);
};
QAction *insertKeyframeAction = menu.addAction(tr("Insert Keyframe"));
connect(insertKeyframeAction, &QAction::triggered, insertKeyframes);
auto deleteKeyframes = [this, event] { deleteSelectedKeyframes(); };
auto deleteKeyframes = [this, event] { m_scene->deleteSelectedKeyframes(); };
QAction *deleteKeyframeAction = menu.addAction(tr("Delete Selected Keyframes"));
connect(deleteKeyframeAction, &QAction::triggered, deleteKeyframes);
if (!hasSelectedKeyframe())
if (!m_scene->hasSelectedKeyframe())
deleteKeyframeAction->setEnabled(false);
menu.exec(event->globalPos());
@@ -445,7 +407,7 @@ QPointF GraphicsView::globalToRaster(const QPoint &point) const
void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
{
m_scene.doNotMoveItems(true);
m_scene->doNotMoveItems(true);
QPointF pivotRaster(globalToRaster(pivot));
@@ -469,9 +431,9 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn);
m_transform = QTransform::fromScale(scaleX, scaleY);
m_scene.setComponentTransform(m_transform);
m_scene->setComponentTransform(m_transform);
QRectF sr = m_scene.sceneRect().adjusted(
QRectF sr = m_scene->rect().adjusted(
-m_style.valueAxisWidth - m_style.canvasMargin,
-m_style.timeAxisHeight - m_style.canvasMargin,
m_style.canvasMargin,
@@ -486,29 +448,7 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
}
m_scene.doNotMoveItems(false);
}
void GraphicsView::insertKeyframe(double time, bool allVisibleCurves)
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
if (allVisibleCurves)
curveItem->insertKeyframeByTime(std::round(time));
else if (curveItem->isUnderMouse())
curveItem->insertKeyframeByTime(std::round(time));
}
}
}
void GraphicsView::deleteSelectedKeyframes()
{
const auto itemList = items();
for (auto *item : itemList) {
if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
curveItem->deleteSelectedKeyframes();
}
m_scene->doNotMoveItems(false);
}
void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
@@ -551,7 +491,7 @@ void GraphicsView::drawExtremaX(QPainter *painter, const QRectF &rect)
void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
{
if (m_scene.empty())
if (m_scene->empty())
return;
auto drawHorizontalLine = [rect, painter](double position) {
@@ -560,8 +500,8 @@ void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
painter->save();
painter->setPen(Qt::blue);
drawHorizontalLine(mapValueToY(m_scene.minimumValue()));
drawHorizontalLine(mapValueToY(m_scene.maximumValue()));
drawHorizontalLine(mapValueToY(m_scene->minimumValue()));
drawHorizontalLine(mapValueToY(m_scene->maximumValue()));
painter->restore();
}

View File

@@ -52,16 +52,12 @@ signals:
public:
GraphicsView(CurveEditorModel *model, QWidget *parent = nullptr);
~GraphicsView() override;
CurveEditorModel *model() const;
CurveEditorStyle editorStyle() const;
bool hasActiveItem() const;
bool hasActiveHandle() const;
bool hasSelectedKeyframe() const;
int mapTimeToX(double time) const;
int mapValueToY(double value) const;
@@ -136,10 +132,6 @@ protected:
private:
void applyZoom(double x, double y, const QPoint &pivot = QPoint());
void insertKeyframe(double time, bool allVisibleCurves = false);
void deleteSelectedKeyframes();
void drawGrid(QPainter *painter, const QRectF &rect);
#if 0
@@ -167,7 +159,7 @@ private:
QTransform m_transform;
GraphicsScene m_scene;
GraphicsScene *m_scene;
CurveEditorModel *m_model;

View File

@@ -64,6 +64,22 @@ int HandleItem::type() const
return Type;
}
bool HandleItem::keyframeSelected() const
{
if (auto *frame = keyframe())
return frame->selected();
return false;
}
KeyframeItem *HandleItem::keyframe() const
{
if (KeyframeItem *parent = qgraphicsitem_cast<KeyframeItem *>(parentItem()))
return parent;
return nullptr;
}
HandleItem::Slot HandleItem::slot() const
{
return m_slot;
@@ -77,7 +93,7 @@ QRectF HandleItem::boundingRect() const
bool HandleItem::contains(const QPointF &point) const
{
if (KeyframeItem *parent = qgraphicsitem_cast<KeyframeItem *>(parentItem())) {
if (KeyframeItem *parent = keyframe()) {
HandleGeometry geom(pos(), m_style);
geom.handle.moveCenter(parent->pos() + pos());
return geom.handle.contains(point);
@@ -130,7 +146,7 @@ void HandleItem::setStyle(const CurveEditorStyle &style)
QVariant HandleItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange) {
if (qgraphicsitem_cast<KeyframeItem *>(parentItem())) {
if (keyframe()) {
QPointF pos = value.toPointF();
if (m_slot == HandleItem::Slot::Left) {
if (pos.x() > 0.0)

View File

@@ -30,6 +30,8 @@
namespace DesignTools {
class KeyframeItem;
class HandleItem : public SelectableItem
{
Q_OBJECT
@@ -53,6 +55,10 @@ public:
void underMouseCallback() override;
bool keyframeSelected() const;
KeyframeItem *keyframe() const;
Slot slot() const;
void setStyle(const CurveEditorStyle &style);

View File

@@ -116,6 +116,27 @@ bool KeyframeItem::hasRightHandle() const
return m_frame.hasRightHandle();
}
bool KeyframeItem::hasActiveHandle() const
{
if (m_left && m_left->activated())
return true;
if (m_right && m_right->activated())
return true;
return false;
}
HandleItem *KeyframeItem::leftHandle() const
{
return m_left;
}
HandleItem *KeyframeItem::rightHandle() const
{
return m_right;
}
QTransform KeyframeItem::transform() const
{
return m_transform;

View File

@@ -72,6 +72,12 @@ public:
bool hasRightHandle() const;
bool hasActiveHandle() const;
HandleItem *leftHandle() const;
HandleItem *rightHandle() const;
QTransform transform() const;
void setHandleVisibility(bool visible);

View File

@@ -83,7 +83,6 @@ SelectableItem::SelectableItem(QGraphicsItem *parent)
: CurveEditorItem(parent)
, m_active(false)
, m_selected(false)
, m_locked(false)
, m_preSelected(SelectionMode::Undefined)
{
setFlag(QGraphicsItem::ItemIsSelectable, false);
@@ -139,7 +138,7 @@ void SelectableItem::setSelected(bool selected)
void SelectableItem::setPreselected(SelectionMode mode)
{
if (m_locked)
if (locked())
return;
m_preSelected = mode;
@@ -158,7 +157,7 @@ void SelectableItem::selectionCallback() {}
void SelectableItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (m_locked)
if (locked())
return;
m_active = true;
@@ -168,7 +167,7 @@ void SelectableItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
void SelectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (m_locked)
if (locked())
return;
if (type() == KeyframeItem::Type && !selected())
@@ -179,7 +178,7 @@ void SelectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
void SelectableItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (m_locked)
if (locked())
return;
m_active = false;

View File

@@ -99,8 +99,6 @@ private:
bool m_selected;
bool m_locked;
SelectionMode m_preSelected;
};

View File

@@ -23,6 +23,7 @@
**
****************************************************************************/
#include "selector.h"
#include "graphicsscene.h"
#include "graphicsview.h"
#include "keyframeitem.h"
#include "playhead.h"
@@ -55,29 +56,40 @@ void Selector::paint(QPainter *painter)
painter->restore();
}
void Selector::mousePress(QMouseEvent *event, GraphicsView *view)
void Selector::mousePress(QMouseEvent *event, GraphicsView *view, GraphicsScene *scene)
{
m_shortcut = Shortcut(event);
if (view->hasActiveHandle())
return;
QPointF click = view->globalToScene(event->globalPos());
if (SelectableItem *sitem = scene->intersect(click)) {
KeyframeItem *kitem = qobject_cast<KeyframeItem *>(sitem);
if (HandleItem *hitem = qobject_cast<HandleItem *>(sitem))
kitem = hitem->keyframe();
if (!kitem->selected()) {
if (select(SelectionTool::Undefined, click, scene))
applyPreSelection(scene);
}
} else {
if (select(SelectionTool::Undefined, click, scene))
applyPreSelection(scene);
// Init selection tools.
m_mouseInit = event->globalPos();
m_mouseCurr = event->globalPos();
QPointF click = view->globalToScene(m_mouseInit);
if (!isOverSelectedKeyframe(click, view))
if (select(SelectionTool::Undefined, click, view))
applyPreSelection(view);
m_lasso = QPainterPath(click);
m_lasso.closeSubpath();
m_rect = QRectF(click, click);
}
}
void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead)
void Selector::mouseMove(QMouseEvent *event,
GraphicsView *view,
GraphicsScene *scene,
Playhead &playhead)
{
if (m_mouseInit.isNull())
return;
@@ -89,10 +101,10 @@ void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playh
if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
|| m_shortcut == m_shortcuts.removeFromSelection
|| m_shortcut == m_shortcuts.toggleSelection) {
if (view->hasActiveItem())
if (scene->hasActiveItem())
return;
select(m_tool, view->globalToScene(event->globalPos()), view);
select(m_tool, view->globalToScene(event->globalPos()), scene);
event->accept();
view->viewport()->update();
@@ -111,11 +123,11 @@ void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playh
}
}
void Selector::mouseRelease(QMouseEvent *event, GraphicsView *view)
void Selector::mouseRelease(QMouseEvent *event, GraphicsScene *scene)
{
Q_UNUSED(event)
applyPreSelection(view);
applyPreSelection(scene);
m_shortcut = Shortcut();
m_mouseInit = QPoint();
@@ -124,50 +136,73 @@ void Selector::mouseRelease(QMouseEvent *event, GraphicsView *view)
m_rect = QRectF();
}
bool Selector::isOverSelectedKeyframe(const QPointF &pos, GraphicsView *view)
bool Selector::isOverMovableItem(const QPointF &pos, GraphicsScene *scene)
{
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))
return frame->selected();
auto intersect = [pos](QGraphicsObject *item) {
return item->mapRectToScene(item->boundingRect()).contains(pos);
};
const auto frames = scene->keyframes();
for (auto *frame : frames) {
if (intersect(frame))
return true;
if (auto *leftHandle = frame->leftHandle()) {
if (intersect(leftHandle))
return true;
}
if (auto *rightHandle = frame->rightHandle()) {
if (intersect(rightHandle))
return true;
}
}
return false;
}
bool Selector::select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view)
bool Selector::isOverSelectedKeyframe(const QPointF &pos, GraphicsScene *scene)
{
auto selectWidthTool = [this, tool](SelectionMode mode, const QPointF &pos, GraphicsView *view) {
const auto frames = scene->selectedKeyframes();
for (auto *frame : frames) {
QRectF frameRect = frame->mapRectToScene(frame->boundingRect());
if (frameRect.contains(pos))
return true;
}
return false;
}
bool Selector::select(const SelectionTool &tool, const QPointF &pos, GraphicsScene *scene)
{
auto selectWidthTool = [this,
tool](SelectionMode mode, const QPointF &pos, GraphicsScene *scene) {
switch (tool) {
case SelectionTool::Lasso:
return lassoSelection(mode, pos, view);
return lassoSelection(mode, pos, scene);
case SelectionTool::Rectangle:
return rectangleSelection(mode, pos, view);
return rectangleSelection(mode, pos, scene);
default:
return pressSelection(mode, pos, view);
return pressSelection(mode, pos, scene);
}
};
if (m_shortcut == m_shortcuts.newSelection) {
clearSelection(view);
return selectWidthTool(SelectionMode::New, pos, view);
clearSelection(scene);
return selectWidthTool(SelectionMode::New, pos, scene);
} else if (m_shortcut == m_shortcuts.addToSelection) {
return selectWidthTool(SelectionMode::Add, pos, view);
return selectWidthTool(SelectionMode::Add, pos, scene);
} else if (m_shortcut == m_shortcuts.removeFromSelection) {
return selectWidthTool(SelectionMode::Remove, pos, view);
return selectWidthTool(SelectionMode::Remove, pos, scene);
} else if (m_shortcut == m_shortcuts.toggleSelection) {
return selectWidthTool(SelectionMode::Toggle, pos, view);
return selectWidthTool(SelectionMode::Toggle, pos, scene);
}
return false;
}
bool Selector::pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
bool Selector::pressSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene)
{
bool out = false;
const auto itemList = view->items();
const auto itemList = scene->items();
for (auto *item : itemList) {
if (auto *frame = qgraphicsitem_cast<KeyframeItem *>(item)) {
QRectF itemRect = frame->mapRectToScene(frame->boundingRect());
@@ -176,15 +211,25 @@ bool Selector::pressSelection(SelectionMode mode, const QPointF &pos, GraphicsVi
out = true;
}
}
if (auto *handle = qgraphicsitem_cast<HandleItem *>(item)) {
QRectF itemRect = handle->mapRectToScene(handle->boundingRect());
if (itemRect.contains(pos)) {
if (auto *frame = handle->keyframe()) {
frame->setPreselected(mode);
out = true;
}
}
}
}
return out;
}
bool Selector::rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
bool Selector::rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene)
{
bool out = false;
m_rect.setBottomRight(pos);
const auto itemList = view->items();
const auto itemList = scene->items();
for (auto *item : itemList) {
if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
if (m_rect.contains(keyframeItem->pos())) {
@@ -198,11 +243,11 @@ bool Selector::rectangleSelection(SelectionMode mode, const QPointF &pos, Graphi
return out;
}
bool Selector::lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view)
bool Selector::lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene)
{
bool out = false;
m_lasso.lineTo(pos);
const auto itemList = view->items();
const auto itemList = scene->items();
for (auto *item : itemList) {
if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
if (m_lasso.contains(keyframeItem->pos())) {
@@ -216,20 +261,22 @@ bool Selector::lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsVi
return out;
}
void Selector::clearSelection(GraphicsView *view)
void Selector::clearSelection(GraphicsScene *scene)
{
const auto itemList = view->items();
const auto itemList = scene->items();
for (auto *item : itemList) {
if (auto *frameItem = qgraphicsitem_cast<KeyframeItem *>(item)) {
frameItem->setPreselected(SelectionMode::Clear);
frameItem->applyPreselection();
frameItem->setActivated(false, HandleItem::Slot::Left);
frameItem->setActivated(false, HandleItem::Slot::Right);
}
}
}
void Selector::applyPreSelection(GraphicsView *view)
void Selector::applyPreSelection(GraphicsScene *scene)
{
const auto itemList = view->items();
const auto itemList = scene->items();
for (auto *item : itemList) {
if (auto *keyframeItem = qgraphicsitem_cast<KeyframeItem *>(item))
keyframeItem->applyPreselection();

View File

@@ -31,6 +31,7 @@
namespace DesignTools {
class GraphicsView;
class GraphicsScene;
class Playhead;
enum class SelectionTool { Undefined, Lasso, Rectangle };
@@ -42,26 +43,28 @@ public:
void paint(QPainter *painter);
void mousePress(QMouseEvent *event, GraphicsView *view);
void mousePress(QMouseEvent *event, GraphicsView *view, GraphicsScene *scene);
void mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playhead);
void mouseMove(QMouseEvent *event, GraphicsView *view, GraphicsScene *scene, Playhead &playhead);
void mouseRelease(QMouseEvent *event, GraphicsView *view);
void mouseRelease(QMouseEvent *event, GraphicsScene *scene);
private:
bool isOverSelectedKeyframe(const QPointF &pos, GraphicsView *view);
bool isOverSelectedKeyframe(const QPointF &pos, GraphicsScene *scene);
bool select(const SelectionTool &tool, const QPointF &pos, GraphicsView *view);
bool isOverMovableItem(const QPointF &pos, GraphicsScene *scene);
bool pressSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
bool select(const SelectionTool &tool, const QPointF &pos, GraphicsScene *scene);
bool rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
bool pressSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene);
bool lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsView *view);
bool rectangleSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene);
void clearSelection(GraphicsView *view);
bool lassoSelection(SelectionMode mode, const QPointF &pos, GraphicsScene *scene);
void applyPreSelection(GraphicsView *view);
void clearSelection(GraphicsScene *scene);
void applyPreSelection(GraphicsScene *scene);
Shortcuts m_shortcuts;

View File

@@ -50,7 +50,7 @@ QRectF bbox(const QRectF &rect, const QTransform &transform);
QPalette singleColorPalette(const QColor &color);
template<typename T>
inline void freeClear(std::vector<T *> &vec)
inline void freeClear(T &vec)
{
for (auto *&el : vec)
delete el;