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

source code of kwidgetsaddons/src/kcapacitybar.cpp