1 /*
2  * Pixmap Dial, a custom Qt4 widget
3  * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the COPYING file
16  */
17 
18 #include "pixmapdial.hpp"
19 
20 #include <cmath>
21 
22 #include <QtCore/QTimer>
23 #include <QtGui/QPainter>
24 #include <QtGui/QPaintEvent>
25 #include <QtGui/QPainterPath>
26 
PixmapDial(QWidget * parent)27 PixmapDial::PixmapDial(QWidget* parent)
28     : QDial(parent),
29       fPixmap(":/bitmaps/dial_01d.png"),
30       fPixmapNum("01"),
31       fCustomPaint(CUSTOM_PAINT_NULL),
32       fOrientation(fPixmap.width() > fPixmap.height() ? HORIZONTAL : VERTICAL),
33       fHovered(false),
34       fHoverStep(HOVER_MIN),
35       fLabel(""),
36       fLabelPos(0.0f, 0.0f),
37       fLabelWidth(0),
38       fLabelHeight(0),
39       fLabelGradient(0, 0, 0, 1)
40 {
41     fLabelFont.setPointSize(6);
42 
43     if (palette().window().color().lightness() > 100)
44     {
45         // Light background
46         const QColor c(palette().dark().color());
47         fColor1    = c;
48         fColor2    = QColor(c.red(), c.green(), c.blue(), 0);
49         fColorT[0] = palette().buttonText().color();
50         fColorT[1] = palette().mid().color();
51     }
52     else
53     {
54         // Dark background
55         fColor1    = QColor(0, 0, 0, 255);
56         fColor2    = QColor(0, 0, 0, 0);
57         fColorT[0] = Qt::white;
58         fColorT[1] = Qt::darkGray;
59     }
60 
61     updateSizes();
62 }
63 
getSize() const64 int PixmapDial::getSize() const
65 {
66     return fSize;
67 }
68 
setCustomPaint(CustomPaint paint)69 void PixmapDial::setCustomPaint(CustomPaint paint)
70 {
71     fCustomPaint = paint;
72     fLabelPos.setY(fSize + fLabelHeight/2);
73     update();
74 }
75 
setEnabled(bool enabled)76 void PixmapDial::setEnabled(bool enabled)
77 {
78     if (isEnabled() != enabled)
79     {
80         fPixmap.load(QString(":/bitmaps/dial_%1%2.png").arg(fPixmapNum).arg(enabled ? "" : "d"));
81         updateSizes();
82         update();
83     }
84     QDial::setEnabled(enabled);
85 }
86 
setLabel(QString label)87 void PixmapDial::setLabel(QString label)
88 {
89     fLabel = label;
90 
91     fLabelWidth  = QFontMetrics(font()).width(label);
92     fLabelHeight = QFontMetrics(font()).height();
93 
94     fLabelPos.setX(float(fSize)/2.0f - float(fLabelWidth)/2.0f);
95     fLabelPos.setY(fSize + fLabelHeight);
96 
97     fLabelGradient.setColorAt(0.0f, fColor1);
98     fLabelGradient.setColorAt(0.6f, fColor1);
99     fLabelGradient.setColorAt(1.0f, fColor2);
100 
101     fLabelGradient.setStart(0, float(fSize)/2.0f);
102     fLabelGradient.setFinalStop(0, fSize + fLabelHeight + 5);
103 
104     fLabelGradientRect = QRectF(float(fSize)/8.0f, float(fSize)/2.0f, float(fSize*6)/8.0f, fSize+fLabelHeight+5);
105     update();
106 }
107 
setPixmap(int pixmapId)108 void PixmapDial::setPixmap(int pixmapId)
109 {
110     fPixmapNum.sprintf("%02i", pixmapId);
111     fPixmap.load(QString(":/bitmaps/dial_%1%2.png").arg(fPixmapNum).arg(isEnabled() ? "" : "d"));
112 
113     if (fPixmap.width() > fPixmap.height())
114         fOrientation = HORIZONTAL;
115     else
116         fOrientation = VERTICAL;
117 
118     updateSizes();
119     update();
120 }
121 
minimumSizeHint() const122 QSize PixmapDial::minimumSizeHint() const
123 {
124     return QSize(fSize, fSize);
125 }
126 
sizeHint() const127 QSize PixmapDial::sizeHint() const
128 {
129     return QSize(fSize, fSize);
130 }
131 
updateSizes()132 void PixmapDial::updateSizes()
133 {
134     fWidth  = fPixmap.width();
135     fHeight = fPixmap.height();
136 
137     if (fWidth < 1)
138         fWidth = 1;
139 
140     if (fHeight < 1)
141         fHeight = 1;
142 
143     if (fOrientation == HORIZONTAL)
144     {
145         fSize  = fHeight;
146         fCount = fWidth/fHeight;
147     }
148     else
149     {
150         fSize  = fWidth;
151         fCount = fHeight/fWidth;
152     }
153 
154     setMinimumSize(fSize, fSize + fLabelHeight + 5);
155     setMaximumSize(fSize, fSize + fLabelHeight + 5);
156 }
157 
enterEvent(QEvent * event)158 void PixmapDial::enterEvent(QEvent* event)
159 {
160     fHovered = true;
161     if (fHoverStep == HOVER_MIN)
162         fHoverStep = HOVER_MIN + 1;
163     QDial::enterEvent(event);
164 }
165 
leaveEvent(QEvent * event)166 void PixmapDial::leaveEvent(QEvent* event)
167 {
168     fHovered = false;
169     if (fHoverStep == HOVER_MAX)
170         fHoverStep = HOVER_MAX - 1;
171     QDial::leaveEvent(event);
172 }
173 
paintEvent(QPaintEvent * event)174 void PixmapDial::paintEvent(QPaintEvent* event)
175 {
176     event->accept();
177 
178     QPainter painter(this);
179     painter.save();
180     painter.setRenderHint(QPainter::Antialiasing, true);
181 
182     if (! fLabel.isEmpty())
183     {
184         if (fCustomPaint == CUSTOM_PAINT_NULL)
185         {
186             painter.setPen(fColor2);
187             painter.setBrush(fLabelGradient);
188             painter.drawRect(fLabelGradientRect);
189         }
190 
191         painter.setFont(fLabelFont);
192         painter.setPen(fColorT[isEnabled() ? 0 : 1]);
193         painter.drawText(fLabelPos, fLabel);
194     }
195 
196     if (isEnabled())
197     {
198         float current = value()-minimum();
199         float divider = maximum()-minimum();
200 
201         if (divider == 0.0f)
202             return;
203 
204         float value = current/divider;
205         QRectF source, target(0.0f, 0.0f, fSize, fSize);
206 
207         int xpos, ypos, per = (fCount-1)*value;
208 
209         if (fOrientation == HORIZONTAL)
210         {
211             xpos = fSize*per;
212             ypos = 0.0f;
213         }
214         else
215         {
216             xpos = 0.0f;
217             ypos = fSize*per;
218         }
219 
220         source = QRectF(xpos, ypos, fSize, fSize);
221         painter.drawPixmap(target, fPixmap, source);
222 
223         // Custom knobs (Dry/Wet and Volume)
224         if (fCustomPaint == CUSTOM_PAINT_CARLA_WET || fCustomPaint == CUSTOM_PAINT_CARLA_VOL)
225         {
226             // knob color
227             QColor colorGreen(0x5D, 0xE7, 0x3D, 191 + fHoverStep*7);
228             QColor colorBlue(0x3E, 0xB8, 0xBE, 191 + fHoverStep*7);
229 
230             // draw small circle
231             QRectF ballRect(8.0f, 8.0f, 15.0f, 15.0f);
232             QPainterPath ballPath;
233             ballPath.addEllipse(ballRect);
234             //painter.drawRect(ballRect);
235             float tmpValue  = (0.375f + 0.75f*value);
236             float ballValue = tmpValue - std::floor(tmpValue);
237             QPointF ballPoint(ballPath.pointAtPercent(ballValue));
238 
239             // draw arc
240             int startAngle = 216*16;
241             int spanAngle  = -252*16*value;
242 
243             if (fCustomPaint == CUSTOM_PAINT_CARLA_WET)
244             {
245                 painter.setBrush(colorBlue);
246                 painter.setPen(QPen(colorBlue, 0));
247                 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2f, 2.2f));
248 
249                 QConicalGradient gradient(15.5f, 15.5f, -45);
250                 gradient.setColorAt(0.0f,   colorBlue);
251                 gradient.setColorAt(0.125f, colorBlue);
252                 gradient.setColorAt(0.625f, colorGreen);
253                 gradient.setColorAt(0.75f,  colorGreen);
254                 gradient.setColorAt(0.76f,  colorGreen);
255                 gradient.setColorAt(1.0f,   colorGreen);
256                 painter.setBrush(gradient);
257                 painter.setPen(QPen(gradient, 3));
258             }
259             else
260             {
261                 painter.setBrush(colorBlue);
262                 painter.setPen(QPen(colorBlue, 0));
263                 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2f, 2.2f));
264 
265                 painter.setBrush(colorBlue);
266                 painter.setPen(QPen(colorBlue, 3));
267             }
268 
269             painter.drawArc(4.0f, 4.0f, 26.0f, 26.0f, startAngle, spanAngle);
270         }
271         // Custom knobs (L and R)
272         else if (fCustomPaint == CUSTOM_PAINT_CARLA_L || fCustomPaint == CUSTOM_PAINT_CARLA_R)
273         {
274             // knob color
275             QColor color(0xAD + fHoverStep*5, 0xD5 + fHoverStep*4, 0x4B + fHoverStep*5);
276 
277             // draw small circle
278             QRectF ballRect(7.0f, 8.0f, 11.0f, 12.0f);
279             QPainterPath ballPath;
280             ballPath.addEllipse(ballRect);
281             //painter.drawRect(ballRect);
282             float tmpValue  = (0.375f + 0.75f*value);
283             float ballValue = tmpValue - std::floor(tmpValue);
284             QPointF ballPoint(ballPath.pointAtPercent(ballValue));
285 
286             painter.setBrush(color);
287             painter.setPen(QPen(color, 0));
288             painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0f, 2.0f));
289 
290             int startAngle, spanAngle;
291 
292             // draw arc
293             if (fCustomPaint == CUSTOM_PAINT_CARLA_L)
294             {
295                 startAngle = 216*16;
296                 spanAngle  = -252.0*16*value;
297             }
298             else if (fCustomPaint == CUSTOM_PAINT_CARLA_R)
299             {
300                 startAngle = 324.0*16;
301                 spanAngle  = 252.0*16*(1.0-value);
302             }
303             else
304                 return;
305 
306             painter.setPen(QPen(color, 2));
307             painter.drawArc(3.5f, 4.5f, 22.0f, 22.0f, startAngle, spanAngle);
308 
309             if (HOVER_MIN < fHoverStep && fHoverStep < HOVER_MAX)
310             {
311                 fHoverStep += fHovered ? 1 : -1;
312                 QTimer::singleShot(20, this, SLOT(update()));
313             }
314         }
315 
316         if (HOVER_MIN < fHoverStep && fHoverStep < HOVER_MAX)
317         {
318             fHoverStep += fHovered ? 1 : -1;
319             QTimer::singleShot(20, this, SLOT(update()));
320         }
321     }
322     else
323     {
324         QRectF target(0.0f, 0.0f, fSize, fSize);
325         painter.drawPixmap(target, fPixmap, target);
326     }
327 
328     painter.restore();
329 }
330 
resizeEvent(QResizeEvent * event)331 void PixmapDial::resizeEvent(QResizeEvent* event)
332 {
333     updateSizes();
334     QDial::resizeEvent(event);
335 }
336