1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kcapacitybar.h"
9#include "kstyleextensions.h"
10
11#include <math.h>
12
13#include <QLinearGradient>
14#include <QPaintEvent>
15#include <QPainter>
16#include <QPainterPath>
17#include <QStyleOptionProgressBar>
18
19#define ROUND_MARGIN 6
20#define VERTICAL_SPACING 1
21
22static const int LightShade = 100;
23static const int MidShade = 200;
24static const int DarkShade = 300;
25
26class KCapacityBarPrivate
27{
28public:
29 KCapacityBarPrivate(KCapacityBar::DrawTextMode drawTextMode)
30 : drawTextMode(drawTextMode)
31 {
32 }
33
34 QString text;
35 int value = 0;
36 bool fillFullBlocks = true;
37 bool continuous = true;
38 int barHeight = 12;
39 Qt::Alignment horizontalTextAlignment = Qt::AlignCenter;
40 QStyle::ControlElement ce_capacityBar = QStyle::ControlElement(0);
41
42 KCapacityBar::DrawTextMode drawTextMode;
43};
44
45KCapacityBar::KCapacityBar(QWidget *parent)
46 : KCapacityBar(DrawTextOutline, parent)
47{
48}
49
50KCapacityBar::KCapacityBar(KCapacityBar::DrawTextMode drawTextMode, QWidget *parent)
51 : QWidget(parent)
52 , d(new KCapacityBarPrivate(drawTextMode))
53{
54 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), widget: this);
55}
56
57KCapacityBar::~KCapacityBar() = default;
58
59void KCapacityBar::setValue(int value)
60{
61 d->value = value;
62 update();
63}
64
65int KCapacityBar::value() const
66{
67 return d->value;
68}
69
70void KCapacityBar::setText(const QString &text)
71{
72 bool updateGeom = d->text.isEmpty() || text.isEmpty();
73 d->text = text;
74 if (updateGeom) {
75 updateGeometry();
76 }
77
78#ifndef QT_NO_ACCESSIBILITY
79 setAccessibleName(text);
80#endif
81
82 update();
83}
84
85QString KCapacityBar::text() const
86{
87 return d->text;
88}
89
90void KCapacityBar::setFillFullBlocks(bool fillFullBlocks)
91{
92 d->fillFullBlocks = fillFullBlocks;
93 update();
94}
95
96bool KCapacityBar::fillFullBlocks() const
97{
98 return d->fillFullBlocks;
99}
100
101void KCapacityBar::setContinuous(bool continuous)
102{
103 d->continuous = continuous;
104 update();
105}
106
107bool KCapacityBar::continuous() const
108{
109 return d->continuous;
110}
111
112void KCapacityBar::setBarHeight(int barHeight)
113{
114 // automatically convert odd values to even. This will make the bar look
115 // better.
116 d->barHeight = (barHeight % 2) ? barHeight + 1 : barHeight;
117 updateGeometry();
118}
119
120int KCapacityBar::barHeight() const
121{
122 return d->barHeight;
123}
124
125void KCapacityBar::setHorizontalTextAlignment(Qt::Alignment horizontalTextAlignment)
126{
127 Qt::Alignment alignment = horizontalTextAlignment;
128
129 // if the value came with any vertical alignment flag, remove it.
130 alignment &= ~Qt::AlignTop;
131 alignment &= ~Qt::AlignBottom;
132 alignment &= ~Qt::AlignVCenter;
133
134 d->horizontalTextAlignment = alignment;
135 update();
136}
137
138Qt::Alignment KCapacityBar::horizontalTextAlignment() const
139{
140 return d->horizontalTextAlignment;
141}
142
143void KCapacityBar::setDrawTextMode(DrawTextMode mode)
144{
145 d->drawTextMode = mode;
146 update();
147}
148
149KCapacityBar::DrawTextMode KCapacityBar::drawTextMode() const
150{
151 return d->drawTextMode;
152}
153
154void KCapacityBar::drawCapacityBar(QPainter *p, const QRect &rect) const
155{
156 drawCapacityBar(p, rect, state: {});
157}
158
159void KCapacityBar::drawCapacityBar(QPainter *p, const QRect &rect, QStyle::State state) const
160{
161 if (d->ce_capacityBar) {
162 QStyleOptionProgressBar opt;
163 opt.initFrom(w: this);
164 opt.rect = rect;
165 opt.minimum = 0;
166 opt.maximum = 100;
167 opt.progress = d->value;
168 opt.state |= (state | QStyle::State_Horizontal);
169 opt.text = d->text;
170 opt.textAlignment = Qt::AlignCenter;
171 opt.textVisible = !d->text.isEmpty();
172 style()->drawControl(element: d->ce_capacityBar, opt: &opt, p, w: this);
173
174 return;
175 }
176
177 p->setRenderHints(hints: QPainter::Antialiasing | QPainter::TextAntialiasing);
178
179 p->save();
180
181 QRect drawRect(rect);
182
183 if (d->drawTextMode == DrawTextOutline) {
184 drawRect.setHeight(d->barHeight);
185 }
186
187 QPainterPath outline;
188 outline.moveTo(x: rect.left() + ROUND_MARGIN / 4 + 1, y: rect.top());
189 outline.lineTo(x: rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, y: rect.top());
190 outline.quadTo(ctrlPtx: rect.left() + drawRect.width() + ROUND_MARGIN / 2,
191 ctrlPty: drawRect.height() / 2 + rect.top(),
192 endPtx: rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1,
193 endPty: drawRect.height() + rect.top());
194 outline.lineTo(x: rect.left() + ROUND_MARGIN / 4 + 1, y: drawRect.height() + rect.top());
195 outline.quadTo(ctrlPtx: -ROUND_MARGIN / 2 + rect.left(), ctrlPty: drawRect.height() / 2 + rect.top(), endPtx: rect.left() + ROUND_MARGIN / 4 + 1, endPty: rect.top());
196 const QColor fillColor = palette().window().color().darker(f: DarkShade);
197 p->fillPath(path: outline, brush: QColor(fillColor.red(), fillColor.green(), fillColor.blue(), 50));
198
199 QRadialGradient bottomGradient(QPointF(rect.width() / 2, drawRect.bottom() + 1), rect.width() / 2);
200 bottomGradient.setColorAt(pos: 0, color: palette().window().color().darker(f: LightShade));
201 bottomGradient.setColorAt(pos: 1, color: Qt::transparent);
202 p->fillRect(QRect(rect.left(), drawRect.bottom() + rect.top(), rect.width(), 1), bottomGradient);
203
204 p->translate(dx: rect.left() + 2, dy: rect.top() + 1);
205
206 drawRect.setWidth(drawRect.width() - 4);
207 drawRect.setHeight(drawRect.height() - 2);
208
209 QPainterPath path;
210 path.moveTo(ROUND_MARGIN / 4, y: 0);
211 path.lineTo(x: drawRect.width() - ROUND_MARGIN / 4, y: 0);
212 path.quadTo(ctrlPtx: drawRect.width() + ROUND_MARGIN / 2, ctrlPty: drawRect.height() / 2, endPtx: drawRect.width() - ROUND_MARGIN / 4, endPty: drawRect.height());
213 path.lineTo(ROUND_MARGIN / 4, y: drawRect.height());
214 path.quadTo(ctrlPtx: -ROUND_MARGIN / 2, ctrlPty: drawRect.height() / 2, ROUND_MARGIN / 4, endPty: 0);
215
216 QLinearGradient linearGradient(0, 0, 0, drawRect.height());
217 linearGradient.setColorAt(pos: 0.5, color: palette().window().color().darker(f: MidShade));
218 linearGradient.setColorAt(pos: 1, color: palette().window().color().darker(f: LightShade));
219 p->fillPath(path, brush: linearGradient);
220
221 p->setBrush(Qt::NoBrush);
222 p->setPen(Qt::NoPen);
223
224 if (d->continuous || !d->fillFullBlocks) {
225 int start = (layoutDirection() == Qt::LeftToRight) ? -1 : (drawRect.width() + 2) - (drawRect.width() + 2) * (d->value / 100.0);
226
227 p->setClipRect(QRect(start, 0, (drawRect.width() + 2) * (d->value / 100.0), drawRect.height()), op: Qt::IntersectClip);
228 }
229
230 int left = (layoutDirection() == Qt::LeftToRight) ? 0 : drawRect.width();
231
232 int right = (layoutDirection() == Qt::LeftToRight) ? drawRect.width() : 0;
233
234 int roundMargin = (layoutDirection() == Qt::LeftToRight) ? ROUND_MARGIN : -ROUND_MARGIN;
235
236 int spacing = 2;
237 int verticalSpacing = VERTICAL_SPACING;
238 int slotWidth = 6;
239 int start = roundMargin / 4;
240
241 QPainterPath internalBar;
242 internalBar.moveTo(x: left + roundMargin / 4, y: 0);
243 internalBar.lineTo(x: right - roundMargin / 4, y: 0);
244 internalBar.quadTo(ctrlPtx: right + roundMargin / 2, ctrlPty: drawRect.height() / 2, endPtx: right - roundMargin / 4, endPty: drawRect.height());
245 internalBar.lineTo(x: left + roundMargin / 4, y: drawRect.height());
246 internalBar.quadTo(ctrlPtx: left - roundMargin / 2, ctrlPty: drawRect.height() / 2, endPtx: left + roundMargin / 4, endPty: 0);
247
248 QLinearGradient fillInternalBar(left, 0, right, 0);
249 fillInternalBar.setColorAt(pos: 0, color: palette().window().color().darker(f: MidShade));
250 fillInternalBar.setColorAt(pos: 0.5, color: palette().window().color().darker(f: LightShade));
251 fillInternalBar.setColorAt(pos: 1, color: palette().window().color().darker(f: MidShade));
252
253 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
254 p->save();
255 p->setOpacity(p->opacity() * 0.7);
256 }
257
258 if (!d->continuous) {
259 int numSlots = (drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) / (slotWidth + spacing);
260 int stopSlot = floor(x: (numSlots + 2) * (d->value / 100.0));
261
262 int plusOffset = d->fillFullBlocks ? ((drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) - (numSlots * (slotWidth + spacing))) / 2.0 : 0;
263
264 if (!d->fillFullBlocks || stopSlot) {
265 QPainterPath firstSlot;
266 firstSlot.moveTo(x: left + roundMargin / 4, y: verticalSpacing);
267 firstSlot.lineTo(x: left + slotWidth + roundMargin / 4 + plusOffset, y: verticalSpacing);
268 firstSlot.lineTo(x: left + slotWidth + roundMargin / 4 + plusOffset, y: drawRect.height() - verticalSpacing);
269 firstSlot.lineTo(x: left + roundMargin / 4, y: drawRect.height() - verticalSpacing);
270 firstSlot.quadTo(ctrlPtx: left, ctrlPty: drawRect.height() / 2, endPtx: left + roundMargin / 4, endPty: verticalSpacing);
271 p->fillPath(path: firstSlot, brush: fillInternalBar);
272 start += slotWidth + spacing + plusOffset;
273
274 bool stopped = false;
275 for (int i = 0; i < numSlots + 1; i++) {
276 if (d->fillFullBlocks && (i == (stopSlot + 1))) {
277 stopped = true;
278 break;
279 }
280 p->fillRect(QRect(rect.left() + start, rect.top() + verticalSpacing, slotWidth, drawRect.height() - verticalSpacing * 2), fillInternalBar);
281 start += slotWidth + spacing;
282 }
283
284 if (!d->fillFullBlocks || (!stopped && (stopSlot != (numSlots + 1)) && (stopSlot != numSlots))) {
285 QPainterPath lastSlot;
286 lastSlot.moveTo(x: start, y: verticalSpacing);
287 lastSlot.lineTo(x: start, y: drawRect.height() - verticalSpacing);
288 lastSlot.lineTo(x: start + slotWidth + plusOffset, y: drawRect.height() - verticalSpacing);
289 lastSlot.quadTo(ctrlPtx: start + roundMargin, ctrlPty: drawRect.height() / 2, endPtx: start + slotWidth + plusOffset, endPty: verticalSpacing);
290 lastSlot.lineTo(x: start, y: verticalSpacing);
291 p->fillPath(path: lastSlot, brush: fillInternalBar);
292 }
293 }
294 } else {
295 p->fillPath(path: internalBar, brush: fillInternalBar);
296 }
297
298 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
299 p->restore();
300 }
301
302 p->save();
303 p->setClipping(false);
304 QRadialGradient topGradient(QPointF(rect.width() / 2, drawRect.top()), rect.width() / 2);
305 const QColor fillTopColor = palette().window().color().darker(f: LightShade);
306 topGradient.setColorAt(pos: 0, color: QColor(fillTopColor.red(), fillTopColor.green(), fillTopColor.blue(), 127));
307 topGradient.setColorAt(pos: 1, color: Qt::transparent);
308 p->fillRect(QRect(rect.left(), rect.top() + drawRect.top(), rect.width(), 2), topGradient);
309 p->restore();
310
311 p->save();
312 p->setClipRect(QRect(-1, 0, rect.width(), drawRect.height() / 2), op: Qt::ReplaceClip);
313 QLinearGradient glassGradient(0, -5, 0, drawRect.height());
314 const QColor fillGlassColor = palette().base().color();
315 glassGradient.setColorAt(pos: 0, color: QColor(fillGlassColor.red(), fillGlassColor.green(), fillGlassColor.blue(), 255));
316 glassGradient.setColorAt(pos: 1, color: Qt::transparent);
317 p->fillPath(path: internalBar, brush: glassGradient);
318 p->restore();
319
320 p->restore();
321
322 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
323 QRect rect(drawRect);
324 rect.setHeight(rect.height() + 4);
325 p->drawText(r: rect, flags: Qt::AlignCenter, text: fontMetrics().elidedText(text: d->text, mode: Qt::ElideRight, width: drawRect.width() - 2 * ROUND_MARGIN));
326 } else {
327 p->drawText(r: rect, flags: Qt::AlignBottom | d->horizontalTextAlignment, text: fontMetrics().elidedText(text: d->text, mode: Qt::ElideRight, width: drawRect.width()));
328 }
329}
330
331QSize KCapacityBar::minimumSizeHint() const
332{
333 int width = fontMetrics().boundingRect(text: d->text).width() + ((d->drawTextMode == KCapacityBar::DrawTextInline) ? ROUND_MARGIN * 2 : 0);
334
335 int height = (d->drawTextMode == KCapacityBar::DrawTextInline) ? qMax(a: fontMetrics().height(), b: d->barHeight)
336 : (d->text.isEmpty() ? 0 : fontMetrics().height() + VERTICAL_SPACING * 2) + d->barHeight;
337
338 if (height % 2) {
339 height++;
340 }
341
342 return QSize(width, height);
343}
344
345void KCapacityBar::paintEvent(QPaintEvent *event)
346{
347 QPainter p(this);
348 p.setClipRect(event->rect());
349 drawCapacityBar(p: &p, rect: contentsRect());
350 p.end();
351}
352
353void KCapacityBar::changeEvent(QEvent *event)
354{
355 QWidget::changeEvent(event);
356 if (event->type() == QEvent::StyleChange) {
357 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), widget: this);
358 }
359}
360
361#include "moc_kcapacitybar.cpp"
362

source code of kwidgetsaddons/src/kcapacitybar.cpp