1 /*
2 * Strawberry Music Player
3 * This file was part of Clementine.
4 * Copyright 2010, David Sansome <me@davidsansome.com>
5 *
6 * Strawberry is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Strawberry is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include "config.h"
22
23 #include <QtGlobal>
24 #include <QWidget>
25 #include <QList>
26 #include <QString>
27 #include <QImage>
28 #include <QFont>
29 #include <QFontMetrics>
30 #include <QPainter>
31 #include <QPainterPath>
32 #include <QPalette>
33 #include <QColor>
34 #include <QBrush>
35 #include <QPen>
36 #include <QPoint>
37 #include <QRect>
38 #include <QSize>
39 #include <QTransform>
40 #include <QLinearGradient>
41
42 #include "core/utilities.h"
43 #include "freespacebar.h"
44
45 class QPaintEvent;
46
47 const int FreeSpaceBar::kBarHeight = 20;
48 const int FreeSpaceBar::kBarBorderRadius = 8;
49 const int FreeSpaceBar::kMarkerSpacing = 32;
50 const int FreeSpaceBar::kLabelBoxSize = 12;
51 const int FreeSpaceBar::kLabelBoxPadding = 4;
52 const int FreeSpaceBar::kLabelSpacing = 16;
53
54 const QRgb FreeSpaceBar::kColorBg1 = qRgb(214, 207, 200);
55 const QRgb FreeSpaceBar::kColorBg2 = qRgb(234, 226, 218);
56 const QRgb FreeSpaceBar::kColorAdd1 = qRgb(136, 180, 229);
57 const QRgb FreeSpaceBar::kColorAdd2 = qRgb(72, 146, 229);
58 const QRgb FreeSpaceBar::kColorBar1 = qRgb(250, 148, 76);
59 const QRgb FreeSpaceBar::kColorBar2 = qRgb(214, 102, 24);
60 const QRgb FreeSpaceBar::kColorBorder = qRgb(174, 168, 162);
61
FreeSpaceBar(QWidget * parent)62 FreeSpaceBar::FreeSpaceBar(QWidget *parent)
63 : QWidget(parent),
64 free_(100),
65 additional_(0),
66 total_(100),
67 free_text_(tr("Available")),
68 additional_text_(tr("New songs")),
69 used_text_(tr("Used")) {
70
71 setMinimumHeight(FreeSpaceBar::sizeHint().height());
72
73 }
74
sizeHint() const75 QSize FreeSpaceBar::sizeHint() const {
76 return QSize(150, kBarHeight + kLabelBoxPadding + fontMetrics().height());
77 }
78
paintEvent(QPaintEvent *)79 void FreeSpaceBar::paintEvent(QPaintEvent*) {
80
81 // Geometry
82 QRect bar_rect(rect());
83 bar_rect.setHeight(kBarHeight);
84
85 QRect reflection_rect(bar_rect);
86 reflection_rect.moveTop(reflection_rect.bottom());
87
88 QRect labels_rect(rect());
89 labels_rect.setTop(labels_rect.top() + kBarHeight + kLabelBoxPadding);
90
91 // Draw the reflection
92 // Create the reflected pixmap
93 QImage reflection(reflection_rect.size(), QImage::Format_ARGB32_Premultiplied);
94 reflection.fill(palette().color(QPalette::Window).rgba());
95 QPainter p(&reflection);
96
97 // Set up the transformation
98 QTransform transform;
99 transform.scale(1.0, -1.0);
100 transform.translate(0.0, -reflection.height());
101 p.setTransform(transform);
102
103 // Draw the bar
104 DrawBar(&p, QRect(QPoint(0, 0), reflection.size()));
105
106 // Make it fade out towards the bottom
107 QLinearGradient fade_gradient(reflection.rect().topLeft(), reflection.rect().bottomLeft());
108 fade_gradient.setColorAt(0.0, QColor(0, 0, 0, 0));
109 fade_gradient.setColorAt(1.0, QColor(0, 0, 0, 128));
110
111 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
112 p.fillRect(reflection.rect(), fade_gradient);
113
114 p.end();
115
116 // Draw on the widget
117 p.begin(this);
118 DrawBar(&p, bar_rect);
119 p.drawImage(reflection_rect, reflection);
120 DrawText(&p, labels_rect);
121
122 }
123
DrawBar(QPainter * p,const QRect r)124 void FreeSpaceBar::DrawBar(QPainter *p, const QRect r) {
125
126 p->setRenderHint(QPainter::Antialiasing, true);
127
128 QRect bar_rect(r);
129 bar_rect.setWidth(static_cast<int>(static_cast<float>(bar_rect.width()) * (static_cast<float>(total_ - free_) / static_cast<float>(total_))));
130
131 QLinearGradient background_gradient(r.topLeft(), r.bottomLeft());
132 background_gradient.setColorAt(0, kColorBg1);
133 background_gradient.setColorAt(1, kColorBg2);
134
135 QLinearGradient bar_gradient(bar_rect.topLeft(), bar_rect.bottomLeft());
136 bar_gradient.setColorAt(0, kColorBar1);
137 bar_gradient.setColorAt(1, kColorBar2);
138
139 // Draw the background
140 p->setPen(Qt::NoPen);
141 p->setBrush(background_gradient);
142 p->drawRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
143
144 // Create a path to use for clipping the bars
145 QPainterPath clip_path;
146 clip_path.addRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
147 p->setClipPath(clip_path);
148
149 // Draw any additional space
150 if (additional_ > 0) {
151 QRect additional_rect(bar_rect);
152 additional_rect.setLeft(bar_rect.right());
153 additional_rect.setWidth(static_cast<int>(static_cast<float>(r.width()) * (static_cast<float>(qMin(free_, additional_)) / total_) + 1));
154
155 QLinearGradient additional_gradient(additional_rect.topLeft(), additional_rect.bottomLeft());
156 additional_gradient.setColorAt(0, kColorAdd1);
157 additional_gradient.setColorAt(1, kColorAdd2);
158
159 p->fillRect(additional_rect, additional_gradient);
160 }
161
162 // Draw the bar foreground
163 p->fillRect(bar_rect, bar_gradient);
164
165 // Draw a border
166 p->setClipping(false);
167 p->setPen(kColorBorder);
168 p->setBrush(Qt::NoBrush);
169 p->drawRoundedRect(r, kBarBorderRadius, kBarBorderRadius);
170
171 // Draw marker lines over the top every few pixels
172 p->setOpacity(0.35);
173 p->setRenderHint(QPainter::Antialiasing, false);
174 p->setPen(QPen(palette().color(QPalette::Light), 1.0));
175 for (int x = r.left() + kMarkerSpacing; x < r.right(); x += kMarkerSpacing) {
176 p->drawLine(x, r.top() + 2, x, r.bottom() - 2);
177 }
178
179 p->setOpacity(1.0);
180
181 }
182
DrawText(QPainter * p,const QRect r)183 void FreeSpaceBar::DrawText(QPainter *p, const QRect r) {
184
185 QFont small_font(font());
186 small_font.setPointSize(small_font.pointSize() - 1);
187 small_font.setBold(true);
188 QFontMetrics small_metrics(small_font);
189 p->setFont(small_font);
190
191 // Work out the geometry for the text
192 QList<Label> labels;
193 labels << Label(TextForSize(used_text_, total_ - free_), kColorBar1);
194 if (additional_ > 0) {
195 labels << Label(TextForSize(additional_text_, additional_), kColorAdd1);
196 }
197 labels << Label(TextForSize(free_text_, free_ - additional_), kColorBg2);
198
199 int text_width = 0;
200 for (const Label &label : labels) {
201 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
202 text_width += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.horizontalAdvance(label.text);
203 #else
204 text_width += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.width(label.text);
205 #endif
206 }
207
208 // Draw the text
209 int x = (r.width() - text_width) / 2;
210
211 p->setRenderHint(QPainter::Antialiasing, false);
212 for (const Label &label : labels) {
213 const bool light = palette().color(QPalette::Base).value() > 128;
214
215 QRect box(x, r.top() + (r.height() - kLabelBoxSize)/2, kLabelBoxSize, kLabelBoxSize);
216 p->setPen(label.color.darker());
217 p->setBrush(label.color);
218 p->drawRect(box);
219
220 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
221 QRect text(x + kLabelBoxSize + kLabelBoxPadding, r.top(), small_metrics.horizontalAdvance(label.text), r.height());
222 #else
223 QRect text(x + kLabelBoxSize + kLabelBoxPadding, r.top(), small_metrics.width(label.text), r.height());
224 #endif
225 p->setPen(light ? label.color.darker() : label.color);
226 p->drawText(text, Qt::AlignCenter, label.text);
227 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
228 x += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.horizontalAdvance(label.text);
229 #else
230 x += kLabelBoxSize + kLabelBoxPadding + kLabelSpacing + small_metrics.width(label.text);
231 #endif
232 }
233
234 }
235
TextForSize(const QString & prefix,const qint64 size)236 QString FreeSpaceBar::TextForSize(const QString &prefix, const qint64 size) {
237
238 QString ret;
239 if (size > 0) {
240 ret = Utilities::PrettySize(size);
241 }
242 else if (size < 0) {
243 ret = "-" + Utilities::PrettySize(-size);
244 }
245 else {
246 ret = "0 MB";
247 }
248
249 if (!prefix.isEmpty()) ret.prepend(prefix + " ");
250
251 return ret;
252
253 }
254