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