1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Design Tooling
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "playhead.h"
27 #include "curveeditormodel.h"
28 #include "graphicsview.h"
29
30 #include <QApplication>
31 #include <QGraphicsScene>
32 #include <QPainter>
33 #include <QPainterPath>
34
35 #include <cmath>
36
37 namespace QmlDesigner {
38
39 constexpr double g_playheadMargin = 5.0;
40
Playhead(GraphicsView * view)41 Playhead::Playhead(GraphicsView *view)
42 : m_frame(0)
43 , m_moving(false)
44 , m_rect()
45 , m_timer()
46 {
47 m_timer.setSingleShot(true);
48 m_timer.setInterval(30);
49 QObject::connect(&m_timer, &QTimer::timeout, view, [this, view]() {
50 if (QApplication::mouseButtons() == Qt::LeftButton)
51 mouseMoveOutOfBounds(view);
52 });
53 }
54
currentFrame() const55 int Playhead::currentFrame() const
56 {
57 return m_frame;
58 }
59
setMoving(bool moving)60 void Playhead::setMoving(bool moving)
61 {
62 m_moving = moving;
63 }
64
moveToFrame(int frame,GraphicsView * view)65 void Playhead::moveToFrame(int frame, GraphicsView *view)
66 {
67 m_frame = frame;
68 m_rect.moveCenter(QPointF(view->mapTimeToX(m_frame), m_rect.center().y()));
69 }
70
resize(GraphicsView * view)71 void Playhead::resize(GraphicsView *view)
72 {
73 QRectF viewRect = view->mapToScene(view->viewport()->rect()).boundingRect();
74
75 CurveEditorStyle style = view->editorStyle();
76
77 QPointF tlr(style.valueAxisWidth, style.timeAxisHeight - style.playhead.width);
78 QPointF brr(style.valueAxisWidth + style.playhead.width, -g_playheadMargin);
79
80 QPointF tl = viewRect.topLeft() + tlr;
81 QPointF br = viewRect.bottomLeft() + brr;
82
83 m_rect = QRectF(tl, br);
84
85 moveToFrame(m_frame, view);
86 }
87
mousePress(const QPointF & pos)88 bool Playhead::mousePress(const QPointF &pos)
89 {
90 QRectF hitRect = m_rect;
91 hitRect.setBottom(hitRect.top() + hitRect.width());
92
93 m_moving = hitRect.contains(pos);
94
95 return m_moving;
96 }
97
mouseMove(const QPointF & pos,GraphicsView * view)98 bool Playhead::mouseMove(const QPointF &pos, GraphicsView *view)
99 {
100 if (m_moving) {
101 CurveEditorStyle style = view->editorStyle();
102
103 QRectF canvas = view->canvasRect().adjusted(0.0, -style.timeAxisHeight, 0.0, 0.0);
104
105 if (canvas.contains(pos)) {
106 int frame = std::round(view->mapXtoTime(pos.x()));
107 view->setCurrentFrame(frame);
108 } else if (!m_timer.isActive())
109 m_timer.start();
110 }
111
112 return m_moving;
113 }
114
mouseMoveOutOfBounds(GraphicsView * view)115 void Playhead::mouseMoveOutOfBounds(GraphicsView *view)
116 {
117 if (QApplication::mouseButtons() != Qt::LeftButton)
118 return;
119
120 CurveEditorStyle style = view->editorStyle();
121 QRectF canvas = view->canvasRect();
122 QPointF pos = view->globalToScene(QCursor::pos());
123
124 if (pos.x() > canvas.right()) {
125 double speed = (pos.x() - canvas.right());
126 double nextCenter = m_rect.center().x() + speed;
127 double frame = std::round(view->mapXtoTime(nextCenter));
128 view->setCurrentFrame(frame);
129
130 double framePosition = view->mapTimeToX(frame);
131 double rightSideOut = framePosition + style.playhead.width / 2.0 + style.canvasMargin;
132 double overshoot = rightSideOut - canvas.right();
133 view->scrollContent(overshoot, 0.0);
134
135 } else if (pos.x() < canvas.left()) {
136 double speed = (canvas.left() - pos.x());
137 double nextCenter = m_rect.center().x() - speed;
138 double frame = std::round(view->mapXtoTime(nextCenter));
139 view->setCurrentFrame(frame);
140
141 double framePosition = view->mapTimeToX(frame);
142 double leftSideOut = framePosition - style.playhead.width / 2.0 - style.canvasMargin;
143 double undershoot = canvas.left() - leftSideOut;
144 view->scrollContent(-undershoot, 0.0);
145 }
146
147 m_timer.start();
148 }
149
mouseRelease(GraphicsView * view)150 void Playhead::mouseRelease(GraphicsView *view)
151 {
152 Q_UNUSED(view);
153 m_moving = false;
154 }
155
paint(QPainter * painter,GraphicsView * view) const156 void Playhead::paint(QPainter *painter, GraphicsView *view) const
157 {
158 CurveEditorStyle style = view->editorStyle();
159
160 painter->save();
161 painter->setPen(style.playhead.color);
162 painter->setRenderHint(QPainter::Antialiasing, true);
163
164 QRectF rect = m_rect;
165 rect.setBottom(m_rect.top() + m_rect.width());
166
167 QPointF top(rect.center().x(), rect.top());
168 QPointF right(rect.right(), rect.top());
169 QPointF bottom(rect.center().x(), rect.bottom());
170 QPointF left(rect.left(), rect.top());
171
172 QLineF rightToBottom(right, bottom);
173 rightToBottom.setLength(style.playhead.radius);
174
175 QLineF leftToBottom(left, bottom);
176 leftToBottom.setLength(style.playhead.radius);
177
178 QPainterPath path(top);
179 path.lineTo(right - QPointF(style.playhead.radius, 0.));
180 path.quadTo(right, rightToBottom.p2());
181 path.lineTo(bottom);
182 path.lineTo(leftToBottom.p2());
183 path.quadTo(left, left + QPointF(style.playhead.radius, 0.));
184 path.closeSubpath();
185
186 painter->fillPath(path, style.playhead.color);
187 painter->drawLine(top + QPointF(0., 5.), QPointF(m_rect.center().x(), m_rect.bottom()));
188
189 painter->restore();
190 }
191
192 } // End namespace QmlDesigner.
193