summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/curveeditor/animationcurve.cpp13
-rw-r--r--src/curveeditor/curveeditorstyle.h36
-rw-r--r--src/curveeditor/detail/curveeditorstyledialog.cpp30
-rw-r--r--src/curveeditor/detail/curveeditorstyledialog.h12
-rw-r--r--src/curveeditor/detail/graphicsscene.cpp34
-rw-r--r--src/curveeditor/detail/graphicsscene.h7
-rw-r--r--src/curveeditor/detail/graphicsview.cpp233
-rw-r--r--src/curveeditor/detail/graphicsview.h46
-rw-r--r--src/curveeditor/detail/playhead.cpp129
-rw-r--r--src/curveeditor/detail/playhead.h21
10 files changed, 437 insertions, 124 deletions
diff --git a/src/curveeditor/animationcurve.cpp b/src/curveeditor/animationcurve.cpp
index acb64ce..8e2d522 100644
--- a/src/curveeditor/animationcurve.cpp
+++ b/src/curveeditor/animationcurve.cpp
@@ -39,7 +39,8 @@ AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
, m_minY(std::numeric_limits<double>::max())
, m_maxY(std::numeric_limits<double>::lowest())
{
- if (m_frames.size() >= 2) {
+ if (isValid()) {
+
for (auto e : extrema()) {
if (m_minY > e.y())
@@ -48,12 +49,20 @@ AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames)
if (m_maxY < e.y())
m_maxY = e.y();
}
+
+ for (auto &frame : qAsConst(m_frames)) {
+ if (frame.position().y() < m_minY)
+ m_minY = frame.position().y();
+
+ if (frame.position().y() > m_maxY)
+ m_maxY = frame.position().y();
+ }
}
}
bool AnimationCurve::isValid() const
{
- return !m_frames.empty();
+ return m_frames.size() >= 2;
}
double AnimationCurve::minimumTime() const
diff --git a/src/curveeditor/curveeditorstyle.h b/src/curveeditor/curveeditorstyle.h
index f60948a..ed82317 100644
--- a/src/curveeditor/curveeditorstyle.h
+++ b/src/curveeditor/curveeditorstyle.h
@@ -30,6 +30,7 @@
#include <QColor>
#include <QDialog>
#include <QIcon>
+#include <QKeySequence>
namespace DesignTools {
@@ -64,47 +65,44 @@ struct CurveItemStyleOption
QColor selectionColor = QColor(200, 200, 200);
};
+struct PlayheadStyleOption
+{
+ double width = 20.0;
+ double radius = 4.0;
+ QColor color = QColor(200, 200, 0);
+};
+
+struct Shortcuts
+{
+ QKeySequence zoom = QKeySequence(Qt::AltModifier, Qt::RightButton);
+ QKeySequence pan = QKeySequence(Qt::AltModifier, Qt::MiddleButton);
+};
+
struct CurveEditorStyle
{
- QBrush backgroundBrush = QBrush(QColor(5, 0, 100));
+ Shortcuts shortcuts;
+ QBrush backgroundBrush = QBrush(QColor(5, 0, 100));
QBrush backgroundAlternateBrush = QBrush(QColor(0, 0, 50));
-
QColor fontColor = QColor(200, 200, 200);
-
QColor gridColor = QColor(128, 128, 128);
-
double canvasMargin = 5.0;
-
int zoomInWidth = 100;
-
int zoomInHeight = 100;
-
double timeAxisHeight = 40.0;
-
double timeOffsetLeft = 10.0;
-
double timeOffsetRight = 10.0;
-
QColor rangeBarColor = QColor(128, 128, 128);
-
QColor rangeBarCapsColor = QColor(50, 50, 255);
-
double valueAxisWidth = 60.0;
-
double valueOffsetTop = 10.0;
-
double valueOffsetBottom = 10.0;
- QColor playheadColor = QColor(200, 200, 0);
-
HandleItemStyleOption handleStyle;
-
KeyframeItemStyleOption keyframeStyle;
-
CurveItemStyleOption curveStyle;
-
TreeItemStyleOption treeItemStyle;
+ PlayheadStyleOption playhead;
};
inline QPixmap pixmapFromIcon(const QIcon &icon, const QSize &size, const QColor &color)
diff --git a/src/curveeditor/detail/curveeditorstyledialog.cpp b/src/curveeditor/detail/curveeditorstyledialog.cpp
index fc34737..7f2e288 100644
--- a/src/curveeditor/detail/curveeditorstyledialog.cpp
+++ b/src/curveeditor/detail/curveeditorstyledialog.cpp
@@ -65,7 +65,6 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
, m_valueAxisWidth(new QDoubleSpinBox())
, m_valueOffsetTop(new QDoubleSpinBox())
, m_valueOffsetBottom(new QDoubleSpinBox())
- , m_playhead(new ColorControl(style.playheadColor))
, m_handleSize(new QDoubleSpinBox())
, m_handleLineWidth(new QDoubleSpinBox())
, m_handleColor(new ColorControl(style.handleStyle.color))
@@ -76,6 +75,11 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
, m_curveWidth(new QDoubleSpinBox())
, m_curveColor(new ColorControl(style.curveStyle.color))
, m_curveSelectionColor(new ColorControl(style.curveStyle.selectionColor))
+ , m_treeMargins(new QDoubleSpinBox())
+ , m_playheadWidth(new QDoubleSpinBox())
+ , m_playheadRadius(new QDoubleSpinBox())
+ , m_playheadColor(new ColorControl(style.playhead.color))
+
{
m_canvasMargin->setValue(style.canvasMargin);
m_zoomInWidth->setValue(style.zoomInWidth);
@@ -90,6 +94,9 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
m_handleLineWidth->setValue(style.handleStyle.lineWidth);
m_keyframeSize->setValue(style.keyframeStyle.size);
m_curveWidth->setValue(style.curveStyle.width);
+ m_treeMargins->setValue(style.treeItemStyle.margins);
+ m_playheadWidth->setValue(style.playhead.width);
+ m_playheadRadius->setValue(style.playhead.radius);
connect(m_printButton, &QPushButton::released, this, &CurveEditorStyleDialog::printStyle);
@@ -115,7 +122,6 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
connect(m_valueAxisWidth, doubleSignal, doubleChanged);
connect(m_valueOffsetTop, doubleSignal, doubleChanged);
connect(m_valueOffsetBottom, doubleSignal, doubleChanged);
- connect(m_playhead, &ColorControl::valueChanged, colorChanged);
connect(m_handleSize, doubleSignal, doubleChanged);
connect(m_handleLineWidth, doubleSignal, doubleChanged);
connect(m_handleColor, &ColorControl::valueChanged, colorChanged);
@@ -126,6 +132,10 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
connect(m_curveWidth, doubleSignal, doubleChanged);
connect(m_curveColor, &ColorControl::valueChanged, colorChanged);
connect(m_curveSelectionColor, &ColorControl::valueChanged, colorChanged);
+ connect(m_treeMargins, doubleSignal, doubleChanged);
+ connect(m_playheadWidth, doubleSignal, doubleChanged);
+ connect(m_playheadRadius, doubleSignal, doubleChanged);
+ connect(m_playheadColor, &ColorControl::valueChanged, colorChanged);
auto *box = new QVBoxLayout;
box->addLayout(createRow("Background Color", m_background));
@@ -143,7 +153,6 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
box->addLayout(createRow("Value Axis Width", m_valueAxisWidth));
box->addLayout(createRow("Value Axis Top Offset", m_valueOffsetTop));
box->addLayout(createRow("Value Axis Bottom Offset", m_valueOffsetBottom));
- box->addLayout(createRow("Playhead Color", m_playhead));
box->addLayout(createRow("Handle Size", m_handleSize));
box->addLayout(createRow("Handle Line Width", m_handleLineWidth));
box->addLayout(createRow("Handle Color", m_handleColor));
@@ -154,6 +163,10 @@ CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget
box->addLayout(createRow("Curve Width", m_curveWidth));
box->addLayout(createRow("Curve Color", m_curveColor));
box->addLayout(createRow("Curve Selection Color", m_curveSelectionColor));
+ box->addLayout(createRow("Treeview margins", m_treeMargins));
+ box->addLayout(createRow("Playhead width", m_playheadWidth));
+ box->addLayout(createRow("Playhead radius", m_playheadRadius));
+ box->addLayout(createRow("Playhead color", m_playheadColor));
box->addWidget(m_printButton);
setLayout(box);
@@ -177,7 +190,6 @@ CurveEditorStyle CurveEditorStyleDialog::style() const
style.valueAxisWidth = m_valueAxisWidth->value();
style.valueOffsetTop = m_valueOffsetTop->value();
style.valueOffsetBottom = m_valueOffsetBottom->value();
- style.playheadColor = m_playhead->value();
style.handleStyle.size = m_handleSize->value();
style.handleStyle.lineWidth = m_handleLineWidth->value();
style.handleStyle.color = m_handleColor->value();
@@ -188,6 +200,11 @@ CurveEditorStyle CurveEditorStyleDialog::style() const
style.curveStyle.width = m_curveWidth->value();
style.curveStyle.color = m_curveColor->value();
style.curveStyle.selectionColor = m_curveSelectionColor->value();
+ style.treeItemStyle.margins = m_treeMargins->value();
+ style.playhead.width = m_playheadWidth->value();
+ style.playhead.radius = m_playheadRadius->value();
+ style.playhead.color = m_playheadColor->value();
+
return style;
}
@@ -224,7 +241,6 @@ void CurveEditorStyleDialog::printStyle()
qDebug().nospace() << "out.valueAxisWidth = " << s.valueAxisWidth << ";";
qDebug().nospace() << "out.valueOffsetTop = " << s.valueOffsetTop << ";";
qDebug().nospace() << "out.valueOffsetBottom = " << s.valueOffsetBottom << ";";
- qDebug().nospace() << "out.playheadColor = " << toString(s.playheadColor) << ";";
qDebug().nospace() << "out.handleStyle.size = " << s.handleStyle.size << ";";
qDebug().nospace() << "out.handleStyle.lineWidth = " << s.handleStyle.lineWidth << ";";
qDebug().nospace() << "out.handleStyle.color = " << toString(s.handleStyle.color) << ";";
@@ -238,6 +254,10 @@ void CurveEditorStyleDialog::printStyle()
qDebug().nospace() << "out.curveStyle.color = " << toString(s.curveStyle.color) << ";";
qDebug().nospace() << "out.curveStyle.selectionColor = "
<< toString(s.curveStyle.selectionColor) << ";";
+ qDebug().nospace() << "out.treeItemStyle.margins = " << s.treeItemStyle.margins << ";";
+ qDebug().nospace() << "out.playheadStyle.width = " << s.playhead.width << ";";
+ qDebug().nospace() << "out.playheadStyle.radius = " << s.playhead.radius << ";";
+ qDebug().nospace() << "out.playheadStyle.color = " << toString(s.playhead.color) << ";";
qDebug().nospace() << "return out;";
qDebug() << "";
}
diff --git a/src/curveeditor/detail/curveeditorstyledialog.h b/src/curveeditor/detail/curveeditorstyledialog.h
index 645f545..ad5a28e 100644
--- a/src/curveeditor/detail/curveeditorstyledialog.h
+++ b/src/curveeditor/detail/curveeditorstyledialog.h
@@ -88,8 +88,6 @@ private:
QDoubleSpinBox *m_valueOffsetBottom;
- ColorControl *m_playhead;
-
// HandleItem
QDoubleSpinBox *m_handleSize;
@@ -112,6 +110,16 @@ private:
ColorControl *m_curveColor;
ColorControl *m_curveSelectionColor;
+
+ // TreeItem
+ QDoubleSpinBox *m_treeMargins;
+
+ // Playhead
+ QDoubleSpinBox *m_playheadWidth;
+
+ QDoubleSpinBox *m_playheadRadius;
+
+ ColorControl *m_playheadColor;
};
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/graphicsscene.cpp b/src/curveeditor/detail/graphicsscene.cpp
index 45e02c8..3fd2b71 100644
--- a/src/curveeditor/detail/graphicsscene.cpp
+++ b/src/curveeditor/detail/graphicsscene.cpp
@@ -26,6 +26,7 @@
#include "graphicsscene.h"
#include "animationcurve.h"
#include "curveitem.h"
+#include "graphicsview.h"
#include <QGraphicsSceneMouseEvent>
@@ -37,6 +38,11 @@ GraphicsScene::GraphicsScene(QObject *parent)
, m_limits()
{}
+bool GraphicsScene::empty() const
+{
+ return items().empty();
+}
+
double GraphicsScene::minimumTime() const
{
return limits().left();
@@ -63,6 +69,24 @@ void GraphicsScene::addCurveItem(CurveItem *item)
addItem(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));
+ }
+
+ if (bounds.isNull()) {
+ if (GraphicsView *gview = graphicsView())
+ bounds = gview->defaultRasterRect();
+ }
+
+ if (bounds.isValid())
+ setSceneRect(bounds);
+}
+
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
@@ -95,6 +119,16 @@ void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
}
}
+GraphicsView *GraphicsScene::graphicsView() const
+{
+ const QList<QGraphicsView *> viewList = views();
+ for (auto &&view : viewList) {
+ if (GraphicsView *gview = qobject_cast<GraphicsView *>(view))
+ return gview;
+ }
+ return nullptr;
+}
+
QRectF GraphicsScene::limits() const
{
if (m_dirty) {
diff --git a/src/curveeditor/detail/graphicsscene.h b/src/curveeditor/detail/graphicsscene.h
index 7f80436..4725d33 100644
--- a/src/curveeditor/detail/graphicsscene.h
+++ b/src/curveeditor/detail/graphicsscene.h
@@ -31,6 +31,7 @@ namespace DesignTools {
class AnimationCurve;
class CurveItem;
+class GraphicsView;
class GraphicsScene : public QGraphicsScene
{
@@ -42,6 +43,8 @@ signals:
public:
GraphicsScene(QObject *parent = nullptr);
+ bool empty() const;
+
double minimumTime() const;
double maximumTime() const;
@@ -52,6 +55,8 @@ public:
void addCurveItem(CurveItem *item);
+ void setComponentTransform(const QTransform& transform);
+
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
@@ -60,6 +65,8 @@ protected:
private:
using QGraphicsScene::addItem;
+ GraphicsView * graphicsView() const;
+
QRectF limits() const;
void clearSelection();
diff --git a/src/curveeditor/detail/graphicsview.cpp b/src/curveeditor/detail/graphicsview.cpp
index 29251aa..245194f 100644
--- a/src/curveeditor/detail/graphicsview.cpp
+++ b/src/curveeditor/detail/graphicsview.cpp
@@ -22,14 +22,18 @@
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
+
#include "graphicsview.h"
#include "curveeditormodel.h"
#include "curveitem.h"
#include "utils.h"
#include <QAction>
+#include <QApplication>
+#include <QKeySequence>
#include <QMenu>
#include <QResizeEvent>
+#include <QScrollBar>
#include <cmath>
@@ -40,7 +44,7 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
, m_scene()
, m_style(model->style())
, m_model(model)
- , m_playhead()
+ , m_playhead(this)
, m_dialog(m_style)
, m_zoomX(0.0)
, m_zoomY(0.0)
@@ -49,6 +53,8 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
setScene(&m_scene);
setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+ setResizeAnchor(QGraphicsView::NoAnchor);
+ setTransformationAnchor(QGraphicsView::NoAnchor);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
@@ -76,6 +82,62 @@ CurveEditorStyle GraphicsView::editorStyle() const
return m_style;
}
+double GraphicsView::minimumTime() const
+{
+ 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();
+}
+
+double GraphicsView::minimumValue() const
+{
+ return m_scene.empty() ? -1.0 : m_scene.minimumValue();
+}
+
+double GraphicsView::maximumValue() const
+{
+ return m_scene.empty() ? 1.0 : m_scene.maximumValue();
+}
+
+QRectF GraphicsView::canvasRect() const
+{
+ QRect r = viewport()->rect().adjusted(
+ m_style.valueAxisWidth + m_style.canvasMargin,
+ m_style.timeAxisHeight + m_style.canvasMargin,
+ -m_style.canvasMargin,
+ -m_style.canvasMargin);
+
+ return mapToScene(r).boundingRect();
+}
+
+QRectF GraphicsView::timeScaleRect() const
+{
+ QRect vp(viewport()->rect());
+ QPoint tl = vp.topLeft() + QPoint(m_style.valueAxisWidth, 0);
+ QPoint br = vp.topRight() + QPoint(0, m_style.timeAxisHeight);
+ return mapToScene(QRect(tl, br)).boundingRect();
+}
+
+QRectF GraphicsView::valueScaleRect() const
+{
+ QRect vp(viewport()->rect());
+ QPoint tl = vp.topLeft() + QPoint(0, m_style.timeAxisHeight);
+ QPoint br = vp.bottomLeft() + QPoint(m_style.valueAxisWidth, 0);
+ return mapToScene(QRect(tl, br)).boundingRect();
+}
+
+QRectF GraphicsView::defaultRasterRect() const
+{
+ QPointF topLeft(mapTimeToX(minimumTime()), mapValueToY(maximumValue()));
+ QPointF bottomRight(mapTimeToX(maximumTime()), mapValueToY(minimumValue()));
+ return QRectF(topLeft, bottomRight);
+}
+
void GraphicsView::setStyle(const CurveEditorStyle &style)
{
m_style = style;
@@ -104,7 +166,8 @@ void GraphicsView::zoomY(double zoom)
void GraphicsView::setCurrentFrame(int frame)
{
- m_playhead.moveToFrame(frame, this);
+ int clampedFrame = clamp(frame, m_model->minimumTime(), m_model->maximumTime());
+ m_playhead.moveToFrame(clampedFrame, this);
viewport()->update();
}
@@ -120,33 +183,81 @@ void GraphicsView::reset(const std::vector<CurveItem *> &items)
void GraphicsView::resizeEvent(QResizeEvent *event)
{
- applyZoom(m_zoomX, m_zoomY);
QGraphicsView::resizeEvent(event);
+ applyZoom(m_zoomX, m_zoomY);
}
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
- QPoint viewPos = viewport()->mapFromGlobal(event->globalPos());
- if (!m_playhead.mousePress(mapToScene(viewPos)))
+ if (event->button() == Qt::LeftButton && event->modifiers().testFlag(Qt::NoModifier)) {
+
+ QPointF pos = mapToScene(event->pos());
+ if (timeScaleRect().contains(pos))
+ setCurrentFrame(std::round(mapXtoTime(pos.x())));
+ }
+
+ QKeySequence eventSequence(event->modifiers(), event->buttons());
+ if (eventSequence == m_style.shortcuts.zoom || eventSequence == m_style.shortcuts.pan) {
+
+ m_mouse = event->globalPos();
+ m_start = event->globalPos();
+ event->accept();
+ return;
+ }
+
+ if (!m_playhead.mousePress(globalToScene(event->globalPos())))
QGraphicsView::mousePressEvent(event);
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
- QPoint viewPos = viewport()->mapFromGlobal(event->globalPos());
- if (!m_playhead.mouseMove(mapToScene(viewPos), this))
+ 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) {
+
+ scrollContent(-delta.x(), delta.y());
+ m_playhead.resize(this);
+ m_mouse = event->globalPos();
+ return;
+ }
+ }
+
+ if (!m_playhead.mouseMove(globalToScene(event->globalPos()), this))
QGraphicsView::mouseMoveEvent(event);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
+ m_mouse = QPoint();
+ m_start = QPoint();
QGraphicsView::mouseReleaseEvent(event);
m_playhead.mouseRelease(this);
}
+void GraphicsView::wheelEvent(QWheelEvent *event)
+{
+ if (event->modifiers().testFlag(Qt::AltModifier))
+ return;
+
+ QGraphicsView::wheelEvent(event);
+}
+
void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
{
- Q_UNUSED(event);
+ if (event->modifiers() != Qt::NoModifier)
+ return;
auto openStyleEditor = [this]() { m_dialog.show(); };
@@ -159,19 +270,17 @@ void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
void GraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
{
- auto gap = QRectF(rect.topLeft(), QSizeF(m_style.valueAxisWidth, m_style.timeAxisHeight));
-
- m_playhead.paint(painter);
-
- auto abscissa = QRectF(gap.topRight(), rect.topRight() + QPointF(0.0, gap.height()));
+ QRectF abscissa = timeScaleRect();
if (abscissa.isValid())
drawTimeScale(painter, abscissa);
- auto ordinate = QRectF(gap.bottomLeft(), rect.bottomLeft() + QPointF(gap.width(), 0.0));
+ auto ordinate = valueScaleRect();
if (ordinate.isValid())
drawValueScale(painter, ordinate);
- painter->fillRect(gap, m_style.backgroundAlternateBrush);
+ m_playhead.paint(painter, this);
+
+ painter->fillRect(QRectF(rect.topLeft(), abscissa.bottomLeft()), m_style.backgroundAlternateBrush);
}
void GraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
@@ -184,67 +293,86 @@ void GraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
drawExtremaY(painter, rect);
}
-int GraphicsView::mapTimeToX(double time)
+int GraphicsView::mapTimeToX(double time) const
{
return std::round(time * scaleX(m_transform));
}
-int GraphicsView::mapValueToY(double y)
+int GraphicsView::mapValueToY(double y) const
{
return std::round(y * scaleY(m_transform));
}
-double GraphicsView::mapXtoTime(int x)
+double GraphicsView::mapXtoTime(int x) const
{
return static_cast<double>(x) / scaleX(m_transform);
}
-double GraphicsView::mapYtoValue(int y)
+double GraphicsView::mapYtoValue(int y) const
{
return static_cast<double>(y) / scaleY(m_transform);
}
-void GraphicsView::applyZoom(double x, double y)
+QPointF GraphicsView::globalToScene(const QPoint &point) const
+{
+ return mapToScene(viewport()->mapFromGlobal(point));
+}
+
+QPointF GraphicsView::globalToRaster(const QPoint &point) const
+{
+ QPointF scene = globalToScene(point);
+ return QPointF(mapXtoTime(scene.x()), mapYtoValue(scene.y()));
+}
+
+void GraphicsView::scrollContent(double x, double y)
{
- m_zoomX = x;
- m_zoomY = 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));
+
+ m_zoomX = clamp(x, 0.0, 1.0);
+ m_zoomY = clamp(y, 0.0, 1.0);
+
+ double minTime = minimumTime();
+ double maxTime = maximumTime();
+
+ double minValue = minimumValue();
+ double maxValue = maximumValue();
- double canvasWidth = rect().width() - m_style.valueAxisWidth - 2 * m_style.canvasMargin - rect().width() / 20.0;
- double xZoomedOut = canvasWidth / (m_model->maximumTime() - m_model->minimumTime());
+ QRectF canvas = canvasRect();
+
+ double xZoomedOut = canvas.width() / (maxTime - minTime);
double xZoomedIn = m_style.zoomInWidth;
double scaleX = lerp(clamp(m_zoomX, 0.0, 1.0), xZoomedOut, xZoomedIn);
- double canvasHeight = rect().height() - m_style.timeAxisHeight - 2 * m_style.canvasMargin - rect().height() / 10;
- double yZoomedOut = canvasHeight / (m_scene.maximumValue() - m_scene.minimumValue());
+ double yZoomedOut = canvas.height() / (maxValue - minValue);
double yZoomedIn = m_style.zoomInHeight;
double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn);
m_transform = QTransform::fromScale(scaleX, scaleY);
- QRectF sceneBounds;
- const auto itemList = items();
- for (auto *item : itemList) {
- if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
- sceneBounds = sceneBounds.united(curveItem->setComponentTransform(m_transform));
- }
-
- updateSceneRect(sceneBounds);
-
- m_playhead.resize(this);
-}
-
-void GraphicsView::updateSceneRect(const QRectF &rect)
-{
- if (rect.isValid())
- scene()->setSceneRect(rect);
+ m_scene.setComponentTransform(m_transform);
- QRectF sr = scene()->sceneRect().adjusted(
+ QRectF sr = m_scene.sceneRect().adjusted(
-m_style.valueAxisWidth - m_style.canvasMargin,
-m_style.timeAxisHeight - m_style.canvasMargin,
m_style.canvasMargin,
m_style.canvasMargin);
setSceneRect(sr);
+
+ m_playhead.resize(this);
+
+ if (!pivot.isNull()) {
+ QPointF deltaTransformed = pivotRaster - globalToRaster(pivot);
+ scrollContent(mapTimeToX(deltaTransformed.x()), mapValueToY(deltaTransformed.y()));
+ }
}
void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
@@ -266,7 +394,7 @@ void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
painter->setPen(m_style.gridColor);
double timeIncrement = timeLabelInterval(painter, m_model->maximumTime());
- for (double i = m_model->minimumTime(); i <= m_model->maximumTime(); i += timeIncrement)
+ for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
drawVerticalLine(mapTimeToX(i));
painter->restore();
@@ -287,6 +415,9 @@ void GraphicsView::drawExtremaX(QPainter *painter, const QRectF &rect)
void GraphicsView::drawExtremaY(QPainter *painter, const QRectF &rect)
{
+ if (m_scene.empty())
+ return;
+
auto drawHorizontalLine = [rect, painter](double position) {
painter->drawLine(rect.left(), position, rect.right(), position);
};
@@ -316,12 +447,13 @@ void GraphicsView::drawTimeScale(QPainter *painter, const QRectF &rect)
QRect textRect = fm.boundingRect(timeText);
textRect.moveCenter(QPoint(position, rect.center().y()));
+
painter->drawText(textRect, Qt::AlignCenter, timeText);
painter->drawLine(position, rect.bottom() - 2, position, textRect.bottom() + 2);
};
- double timeIncrement = timeLabelInterval(painter, m_model->maximumTime());
- for (double i = m_model->minimumTime(); i <= m_model->maximumTime(); i += timeIncrement)
+ double timeIncrement = timeLabelInterval(painter, maximumTime());
+ for (double i = minimumTime(); i <= maximumTime(); i += timeIncrement)
paintLabeledTick(i);
painter->restore();
@@ -344,8 +476,8 @@ void GraphicsView::drawValueScale(QPainter *painter, const QRectF &rect)
painter->drawText(textRect, Qt::AlignCenter, valueText);
};
- paintLabeledTick(m_scene.minimumValue());
- paintLabeledTick(m_scene.maximumValue());
+ paintLabeledTick(minimumValue());
+ paintLabeledTick(maximumValue());
painter->restore();
}
@@ -358,7 +490,11 @@ double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
int nextFactor = 5;
double tickDistance = mapTimeToX(deltaTime);
+
while (true) {
+ if (tickDistance == 0 && deltaTime > maxTime)
+ return maxTime;
+
if (tickDistance > minTextSpacing)
break;
@@ -370,6 +506,7 @@ double GraphicsView::timeLabelInterval(QPainter *painter, double maxTime)
else
nextFactor = 5;
}
+
return deltaTime;
}
diff --git a/src/curveeditor/detail/graphicsview.h b/src/curveeditor/detail/graphicsview.h
index 8d6c5f7..057476b 100644
--- a/src/curveeditor/detail/graphicsview.h
+++ b/src/curveeditor/detail/graphicsview.h
@@ -51,6 +51,34 @@ public:
CurveEditorStyle editorStyle() const;
+ int mapTimeToX(double time) const;
+
+ int mapValueToY(double value) const;
+
+ double mapXtoTime(int x) const;
+
+ double mapYtoValue(int y) const;
+
+ QPointF globalToScene(const QPoint &point) const;
+
+ QPointF globalToRaster(const QPoint &point) const;
+
+ double minimumTime() const;
+
+ double maximumTime() const;
+
+ double minimumValue() const;
+
+ double maximumValue() const;
+
+ QRectF canvasRect() const;
+
+ QRectF timeScaleRect() const;
+
+ QRectF valueScaleRect() const;
+
+ QRectF defaultRasterRect() const;
+
void setStyle(const CurveEditorStyle &style);
void zoomX(double zoom);
@@ -70,6 +98,8 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+
void contextMenuEvent(QContextMenuEvent *event) override;
void drawForeground(QPainter *painter, const QRectF &rect) override;
@@ -77,17 +107,9 @@ protected:
void drawBackground(QPainter *painter, const QRectF &rect) override;
private:
- int mapTimeToX(double time);
-
- int mapValueToY(double value);
+ void scrollContent(double x, double y);
- double mapXtoTime(int x);
-
- double mapYtoValue(int y);
-
- void applyZoom(double x, double y);
-
- void updateSceneRect(const QRectF &rect = QRectF());
+ void applyZoom(double x, double y, const QPoint &pivot = QPoint());
void drawGrid(QPainter *painter, const QRectF &rect);
@@ -117,6 +139,10 @@ private:
double m_zoomY;
QTransform m_transform;
+
+ QPoint m_mouse;
+
+ QPoint m_start;
};
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/playhead.cpp b/src/curveeditor/detail/playhead.cpp
index b829cf9..15edb4b 100644
--- a/src/curveeditor/detail/playhead.cpp
+++ b/src/curveeditor/detail/playhead.cpp
@@ -27,6 +27,7 @@
#include "curveeditormodel.h"
#include "graphicsview.h"
+#include <QApplication>
#include <QGraphicsScene>
#include <QPainter>
@@ -34,10 +35,26 @@
namespace DesignTools {
-Playhead::Playhead()
+constexpr double g_playheadMargin = 5.0;
+
+Playhead::Playhead(GraphicsView *view)
: m_frame(0)
+ , m_moving(false)
, m_rect()
-{}
+ , m_timer()
+{
+ m_timer.setSingleShot(true);
+ m_timer.setInterval(30);
+ QObject::connect(&m_timer, &QTimer::timeout, view, [this, view]() {
+ if (QApplication::mouseButtons() == Qt::LeftButton)
+ mouseMoveOutOfBounds(view);
+ });
+}
+
+int Playhead::currentFrame() const
+{
+ return m_frame;
+}
void Playhead::moveToFrame(int frame, GraphicsView *view)
{
@@ -51,9 +68,10 @@ void Playhead::resize(GraphicsView *view)
CurveEditorStyle style = view->editorStyle();
- QPointF topLeft = viewRect.topLeft() + QPointF(style.valueAxisWidth, style.timeAxisHeight + 5.);
+ QPointF tl = viewRect.topLeft() + QPointF(style.valueAxisWidth, style.timeAxisHeight - style.playhead.width);
+ QPointF br = viewRect.bottomLeft() + QPointF(style.valueAxisWidth + style.playhead.width, -g_playheadMargin);
- m_rect = QRectF(topLeft, QSizeF(20., viewRect.height() - style.timeAxisHeight - 10.));
+ m_rect = QRectF(tl, br);
moveToFrame(m_frame, view);
}
@@ -63,45 +81,94 @@ bool Playhead::mousePress(const QPointF &pos)
QRectF hitRect = m_rect;
hitRect.setBottom(hitRect.top() + hitRect.width());
- if (hitRect.contains(pos)) {
- m_mouse = pos;
- m_center = m_rect.center().x();
- return true;
- } else {
- m_mouse = QPointF();
- m_center = 0.0;
- }
- return false;
+ m_moving = hitRect.contains(pos);
+
+ return m_moving;
}
bool Playhead::mouseMove(const QPointF &pos, GraphicsView *view)
{
- if (!m_mouse.isNull()) {
- m_center += pos.x() - m_mouse.x();
- m_mouse = pos;
- view->setCurrentFrame(std::round(view->mapXtoTime(m_center)));
- return true;
+ if (m_moving) {
+ CurveEditorStyle style = view->editorStyle();
+
+ QRectF canvas = view->canvasRect().adjusted(0.0, -style.timeAxisHeight, 0.0, 0.0);
+
+ if (canvas.contains(pos))
+ view->setCurrentFrame(std::round(view->mapXtoTime(pos.x())));
+ else if (!m_timer.isActive())
+ m_timer.start();
}
- return false;
+
+ return m_moving;
+}
+
+void Playhead::mouseMoveOutOfBounds(GraphicsView *view)
+{
+ if (QApplication::mouseButtons() != Qt::LeftButton)
+ 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 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 leftSideOut = framePosition - style.playhead.width / 2.0 - style.canvasMargin;
+
+ double undershoot = canvas.left() - leftSideOut;
+
+ view->scrollContent(-undershoot, 0.0);
+ }
+
+ m_timer.start();
}
void Playhead::mouseRelease(GraphicsView *view)
{
- if (!m_mouse.isNull())
+ if (m_moving)
emit view->model()->currentFrameChanged(m_frame);
- m_mouse = QPointF();
- m_center = 0.0;
+ m_moving = false;
}
-void Playhead::paint(QPainter *painter) const
+void Playhead::paint(QPainter *painter, GraphicsView *view) const
{
+ CurveEditorStyle style = view->editorStyle();
+
painter->save();
- painter->setPen(Qt::yellow);
+ painter->setPen(style.playhead.color);
painter->setRenderHint(QPainter::Antialiasing, true);
- double radius = 6.0;
-
QRectF rect = m_rect;
rect.setBottom(m_rect.top() + m_rect.width());
@@ -111,20 +178,20 @@ void Playhead::paint(QPainter *painter) const
QPointF left(rect.left(), rect.top());
QLineF rightToBottom(right, bottom);
- rightToBottom.setLength(radius);
+ rightToBottom.setLength(style.playhead.radius);
QLineF leftToBottom(left, bottom);
- leftToBottom.setLength(radius);
+ leftToBottom.setLength(style.playhead.radius);
QPainterPath path(top);
- path.lineTo(right - QPointF(radius, 0.));
+ path.lineTo(right - QPointF(style.playhead.radius, 0.));
path.quadTo(right, rightToBottom.p2());
path.lineTo(bottom);
path.lineTo(leftToBottom.p2());
- path.quadTo(left, left + QPointF(radius, 0.));
+ path.quadTo(left, left + QPointF(style.playhead.radius, 0.));
path.closeSubpath();
- painter->fillPath(path, Qt::yellow);
+ painter->fillPath(path, style.playhead.color);
painter->drawLine(top + QPointF(0., 5.), QPointF(m_rect.center().x(), m_rect.bottom()));
diff --git a/src/curveeditor/detail/playhead.h b/src/curveeditor/detail/playhead.h
index f82a04d..ffb4da4 100644
--- a/src/curveeditor/detail/playhead.h
+++ b/src/curveeditor/detail/playhead.h
@@ -25,7 +25,10 @@
#pragma once
-#include <QGraphicsObject>
+#include <QRectF>
+#include <QTimer>
+
+class QPainter;
namespace DesignTools {
@@ -33,12 +36,12 @@ class GraphicsView;
class Playhead
{
- friend class GraphicsView;
-
public:
- Playhead();
+ Playhead(GraphicsView *view);
+
+ int currentFrame() const;
- void paint(QPainter *painter) const;
+ void paint(QPainter *painter, GraphicsView *view) const;
void moveToFrame(int frame, GraphicsView *view);
@@ -51,13 +54,17 @@ public:
void mouseRelease(GraphicsView *view);
private:
+ void mouseMoveOutOfBounds(GraphicsView *view);
+
int m_frame;
- double m_center;
+ bool m_moving;
QRectF m_rect;
- QPointF m_mouse;
+ QTimer m_timer;
+
+ GraphicsView *m_view;
};
} // End namespace DesignTools.