Engauge Digitizer 2
Loading...
Searching...
No Matches
ViewProfileDivider.cpp
Go to the documentation of this file.
1/******************************************************************************************************
2 * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3 * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4 * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5 ******************************************************************************************************/
6
7#include <QBrush>
8#include <QCursor>
9#include <QDebug>
10#include <QGraphicsLineItem>
11#include <QGraphicsPolygonItem>
12#include <QGraphicsScene>
13#include <QGraphicsSceneMouseEvent>
14#include <QGraphicsView>
15#include <QPen>
16#include "ViewProfileDivider.h"
17
18const double ARROW_WIDTH = 4.0;
19const double ARROW_HEIGHT = 5.0;
20const double DIVIDER_WIDTH = 0.0; // Zero value gives a concise line that is a single pixel wide
21const int PADDLE_HEIGHT = 10;
22const int PADDLE_WIDTH = 10;
23const double SHADED_AREA_OPACITY = 0.4;
24const int X_INITIAL = 0;
25const int SLOP = 2; // Pixels of shading added at each boundary to prevent a gap
26const QColor ARROW_COLOR (Qt::NoPen);
27const QColor SHADED_AREA_COLOR = QColor (220, 220, 220); // Light gray
28const QColor DIVIDER_COLOR = QColor (140, 140, 255); // Slightly darker gray
29
31 QGraphicsView &view,
32 int sceneWidth,
33 int sceneHeight,
34 int yCenter,
35 bool isLowerBoundary) :
36 QGraphicsRectItem (X_INITIAL,
37 0,
40 m_view (view),
41 m_yCenter (yCenter),
42 m_divider (nullptr),
43 m_shadedArea (nullptr),
44 m_sceneWidth (sceneWidth),
45 m_sceneHeight (sceneHeight),
46 m_isLowerBoundary (isLowerBoundary)
47{
48 // Initial positions will not appear since they are overridden by setX
49
50 // Paddle
51 setVisible (true);
52 setPen (QPen (DIVIDER_COLOR));
53 setBrush (QBrush (QColor (140, 255, 140)));
54 setOpacity (1.0);
55 scene.addItem (this);
56 setFlags (QGraphicsItem::ItemIsMovable |
57 QGraphicsItem::ItemSendsGeometryChanges);
58 setCursor (Qt::OpenHandCursor);
59 setZValue (2.0);
60
61 // Arrow on paddle
62 m_arrow = new QGraphicsPolygonItem (this);
63
64 // Shaded area
65 m_shadedArea = new QGraphicsRectItem (X_INITIAL,
66 0,
67 0,
68 sceneHeight - 1);
69 m_shadedArea->setOpacity (SHADED_AREA_OPACITY);
70 m_shadedArea->setBrush (QBrush (SHADED_AREA_COLOR));
71 m_shadedArea->setPen (Qt::NoPen);
72 m_shadedArea->setZValue (0.0);
73 scene.addItem (m_shadedArea);
74
75 // Vertical divider. This is not made a child of the paddle since that will force the divider
76 // to always be drawn above the paddle, rather than underneath the paddle as we want. Even setting
77 // the z values will not succeed in drawing the divider under the paddle if they are child-parent.
78 m_divider = new QGraphicsLineItem (X_INITIAL,
79 -SLOP,
81 2 * SLOP + sceneHeight);
82 m_divider->setPen (QPen (QBrush (DIVIDER_COLOR), DIVIDER_WIDTH));
83 m_divider->setZValue (1.0);
84 scene.addItem (m_divider);
85}
86
87QVariant ViewProfileDivider::itemChange (GraphicsItemChange change, const QVariant &value)
88{
89 if (change == ItemPositionChange && scene ()) {
90
91 // Clip x coordinate, in pixel coordinates. Y coordinate stays the same (by setting delta to zero)
92 QPointF newPos = QPointF (value.toPointF().x(), 0.0) + m_startDragPos;
93 double newX = newPos.x();
94 newX = qMax (newX, 0.0);
95 newX = qMin (newX, double (m_sceneWidth));
96 newPos.setX (newX);
97 newPos -= m_startDragPos; // Change from absolute coordinates back to relative coordinates
98
99 // Before returning newPos for the paddle, we apply its movement to the divider and shaded area
100 m_xScene = newX;
101 updateGeometryDivider();
102 updateGeometryNonPaddle ();
103
104 sendSignalMoved ();
105
106 return newPos;
107 }
108
109 return QGraphicsRectItem::itemChange (change, value);
110}
111
112void ViewProfileDivider::mousePressEvent(QGraphicsSceneMouseEvent * /* event */)
113{
114 // Since the cursor position is probably not in the center of the paddle, we save the paddle center
115 m_startDragPos = QPointF (rect().x () + rect().width () / 2.0,
116 rect().y () + rect().height () / 2.0);
117}
118
119void ViewProfileDivider::sendSignalMoved ()
120{
121 if (m_isLowerBoundary) {
122 emit signalMovedLow (m_xScene);
123 } else {
124 emit signalMovedHigh (m_xScene);
125 }
126}
127
129 double xLow,
130 double xHigh)
131{
132 // Convert to screen coordinates
133 m_xScene = m_sceneWidth * (x - xLow) / (xHigh - xLow);
134 sendSignalMoved ();
135
136 updateGeometryPaddle ();
137 updateGeometryDivider ();
138 updateGeometryNonPaddle ();
139
140 // Triangle vertices
141 double xLeft = rect().left() + rect().width() / 2.0 - ARROW_WIDTH / 2.0;
142 double xRight = rect().left() + rect().width() / 2.0 + ARROW_WIDTH / 2.0;
143 double yTop = rect().top() + rect().height() / 2.0 - ARROW_HEIGHT / 2.0;
144 double yMiddle = rect().top() + rect().height() / 2.0;
145 double yBottom = rect().top() + rect().height() / 2.0 + ARROW_HEIGHT / 2.0;
146
147 QPolygonF polygonArrow;
148 if (m_isLowerBoundary) {
149
150 // Draw arrow pointing to the right
151 polygonArrow.push_front (QPointF (xLeft, yTop));
152 polygonArrow.push_front (QPointF (xRight, yMiddle));
153 polygonArrow.push_front (QPointF (xLeft, yBottom));
154
155 } else {
156
157 // Draw arrow pointing to the left
158 polygonArrow.push_front (QPointF (xRight, yTop));
159 polygonArrow.push_front (QPointF (xLeft, yMiddle));
160 polygonArrow.push_front (QPointF (xRight, yBottom));
161 }
162 m_arrow->setPolygon (polygonArrow);
163 m_arrow->setPen (QPen (Qt::black));
164 m_arrow->setBrush (QBrush (ARROW_COLOR));
165}
166
167void ViewProfileDivider::slotOtherMoved(double xSceneOther)
168{
169 m_xSceneOther = xSceneOther;
170 updateGeometryNonPaddle ();
171}
172
173void ViewProfileDivider::updateGeometryDivider ()
174{
175 m_divider->setLine (m_xScene,
176 -SLOP,
177 m_xScene,
178 2 * SLOP + m_sceneHeight);
179}
180
181void ViewProfileDivider::updateGeometryNonPaddle()
182{
183 if (m_isLowerBoundary) {
184 if (m_xScene <= m_xSceneOther) {
185
186 // There is one unshaded region in the center
187 m_shadedArea->setRect (-SLOP,
188 -SLOP,
189 SLOP + m_xScene,
190 2 * SLOP + m_sceneHeight);
191
192 } else {
193
194 // There are two unshaded regions on the two sides
195 m_shadedArea->setRect (m_xSceneOther,
196 -SLOP,
197 m_xScene - m_xSceneOther,
198 2 * SLOP + m_sceneHeight);
199
200 }
201 } else {
202
203 if (m_xSceneOther <= m_xScene) {
204
205 // There are two unshaded regions on the two sides
206 m_shadedArea->setRect (m_xScene,
207 -SLOP,
208 SLOP + m_sceneWidth - m_xScene,
209 2 * SLOP + m_sceneHeight);
210
211 } else {
212
213 // There is one unshaded region in the center. To prevent extra-dark shading due to having two
214 // overlapping shaded areas, this shaded area is given zero extent
215 m_shadedArea->setRect (m_xSceneOther,
216 -SLOP,
217 0,
218 2 * SLOP + m_sceneHeight);
219 }
220 }
221}
222
223void ViewProfileDivider::updateGeometryPaddle ()
224{
225 setRect (m_xScene - PADDLE_WIDTH / 2,
226 m_yCenter - PADDLE_HEIGHT / 2,
229}
const double DIVIDER_WIDTH
const double ARROW_WIDTH
const QColor DIVIDER_COLOR
const int PADDLE_HEIGHT
const QColor ARROW_COLOR(Qt::NoPen)
const int X_INITIAL
const double SHADED_AREA_OPACITY
const QColor SHADED_AREA_COLOR
const int PADDLE_WIDTH
const double ARROW_HEIGHT
const int SLOP
void signalMovedHigh(double xSceneOther)
Signal used when divider is dragged and m_isLowerBoundary is false.
void signalMovedLow(double xSceneOther)
Signal used when divider is dragged and m_isLowerBoundary is true.
void setX(double x, double xLow, double xHigh)
Set the position by specifying the new x coordinate.
ViewProfileDivider(QGraphicsScene &scene, QGraphicsView &view, int sceneWidth, int sceneHeight, int yCenter, bool isLowerBoundary)
Single constructor.
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event)
Save paddle position at start of click-and-drag.
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value)
Intercept changes so divider movement can be restricted to horizontal direction only.