KWidgetsAddons

kcapacitybar.cpp
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;
42
43 KCapacityBar::DrawTextMode drawTextMode;
44};
45
50
53 , d(new KCapacityBarPrivate(drawTextMode))
54{
55 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this);
56}
57
58KCapacityBar::~KCapacityBar() = default;
59
61{
62 d->value = value;
63 update();
64}
65
66int KCapacityBar::value() const
67{
68 return d->value;
69}
70
72{
73 bool updateGeom = d->text.isEmpty() || text.isEmpty();
74 d->text = text;
75 if (updateGeom) {
77 }
78
79#ifndef QT_NO_ACCESSIBILITY
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;
119}
120
121int KCapacityBar::barHeight() const
122{
123 return d->barHeight;
124}
125
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
145{
146 d->drawTextMode = mode;
147 update();
148}
149
150KCapacityBar::DrawTextMode KCapacityBar::drawTextMode() const
151{
152 return d->drawTextMode;
153}
154
156{
157 drawCapacityBar(p, rect, {});
158}
159
161{
162 if (d->ce_capacityBar) {
164 opt.initFrom(this);
165 opt.rect = rect;
166 opt.minimum = 0;
167 opt.maximum = 100;
168 opt.progress = d->value;
169 opt.state |= (state | QStyle::State_Horizontal);
170 opt.text = d->text;
171 opt.textAlignment = Qt::AlignCenter;
172 opt.textVisible = !d->text.isEmpty();
173 style()->drawControl(d->ce_capacityBar, &opt, p, this);
174
175 return;
176 }
177
179
180 p->save();
181
182 QRect drawRect(rect);
183
184 if (d->drawTextMode == DrawTextOutline) {
185 drawRect.setHeight(d->barHeight);
186 }
187
188 QPainterPath outline;
189 outline.moveTo(rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
190 outline.lineTo(rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, rect.top());
191 outline.quadTo(rect.left() + drawRect.width() + ROUND_MARGIN / 2,
192 drawRect.height() / 2 + rect.top(),
193 rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1,
194 drawRect.height() + rect.top());
195 outline.lineTo(rect.left() + ROUND_MARGIN / 4 + 1, drawRect.height() + rect.top());
196 outline.quadTo(-ROUND_MARGIN / 2 + rect.left(), drawRect.height() / 2 + rect.top(), rect.left() + ROUND_MARGIN / 4 + 1, rect.top());
197 const QColor fillColor = palette().window().color().darker(DarkShade);
198 p->fillPath(outline, QColor(fillColor.red(), fillColor.green(), fillColor.blue(), 50));
199
200 QRadialGradient bottomGradient(QPointF(rect.width() / 2, drawRect.bottom() + 1), rect.width() / 2);
201 bottomGradient.setColorAt(0, palette().window().color().darker(LightShade));
202 bottomGradient.setColorAt(1, Qt::transparent);
203 p->fillRect(QRect(rect.left(), drawRect.bottom() + rect.top(), rect.width(), 1), bottomGradient);
204
205 p->translate(rect.left() + 2, rect.top() + 1);
206
207 drawRect.setWidth(drawRect.width() - 4);
208 drawRect.setHeight(drawRect.height() - 2);
209
210 QPainterPath path;
211 path.moveTo(ROUND_MARGIN / 4, 0);
212 path.lineTo(drawRect.width() - ROUND_MARGIN / 4, 0);
213 path.quadTo(drawRect.width() + ROUND_MARGIN / 2, drawRect.height() / 2, drawRect.width() - ROUND_MARGIN / 4, drawRect.height());
214 path.lineTo(ROUND_MARGIN / 4, drawRect.height());
215 path.quadTo(-ROUND_MARGIN / 2, drawRect.height() / 2, ROUND_MARGIN / 4, 0);
216
217 QLinearGradient linearGradient(0, 0, 0, drawRect.height());
218 linearGradient.setColorAt(0.5, palette().window().color().darker(MidShade));
219 linearGradient.setColorAt(1, palette().window().color().darker(LightShade));
220 p->fillPath(path, linearGradient);
221
223 p->setPen(Qt::NoPen);
224
225 if (d->continuous || !d->fillFullBlocks) {
226 int start = (layoutDirection() == Qt::LeftToRight) ? -1 : (drawRect.width() + 2) - (drawRect.width() + 2) * (d->value / 100.0);
227
228 p->setClipRect(QRect(start, 0, (drawRect.width() + 2) * (d->value / 100.0), drawRect.height()), Qt::IntersectClip);
229 }
230
231 int left = (layoutDirection() == Qt::LeftToRight) ? 0 : drawRect.width();
232
233 int right = (layoutDirection() == Qt::LeftToRight) ? drawRect.width() : 0;
234
235 int roundMargin = (layoutDirection() == Qt::LeftToRight) ? ROUND_MARGIN : -ROUND_MARGIN;
236
237 int spacing = 2;
238 int verticalSpacing = VERTICAL_SPACING;
239 int slotWidth = 6;
240 int start = roundMargin / 4;
241
242 QPainterPath internalBar;
243 internalBar.moveTo(left + roundMargin / 4, 0);
244 internalBar.lineTo(right - roundMargin / 4, 0);
245 internalBar.quadTo(right + roundMargin / 2, drawRect.height() / 2, right - roundMargin / 4, drawRect.height());
246 internalBar.lineTo(left + roundMargin / 4, drawRect.height());
247 internalBar.quadTo(left - roundMargin / 2, drawRect.height() / 2, left + roundMargin / 4, 0);
248
249 QLinearGradient fillInternalBar(left, 0, right, 0);
250 fillInternalBar.setColorAt(0, palette().window().color().darker(MidShade));
251 fillInternalBar.setColorAt(0.5, palette().window().color().darker(LightShade));
252 fillInternalBar.setColorAt(1, palette().window().color().darker(MidShade));
253
254 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
255 p->save();
256 p->setOpacity(p->opacity() * 0.7);
257 }
258
259 if (!d->continuous) {
260 int numSlots = (drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) / (slotWidth + spacing);
261 int stopSlot = floor((numSlots + 2) * (d->value / 100.0));
262
263 int plusOffset = d->fillFullBlocks ? ((drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) - (numSlots * (slotWidth + spacing))) / 2.0 : 0;
264
265 if (!d->fillFullBlocks || stopSlot) {
266 QPainterPath firstSlot;
267 firstSlot.moveTo(left + roundMargin / 4, verticalSpacing);
268 firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, verticalSpacing);
269 firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, drawRect.height() - verticalSpacing);
270 firstSlot.lineTo(left + roundMargin / 4, drawRect.height() - verticalSpacing);
271 firstSlot.quadTo(left, drawRect.height() / 2, left + roundMargin / 4, verticalSpacing);
272 p->fillPath(firstSlot, fillInternalBar);
273 start += slotWidth + spacing + plusOffset;
274
275 bool stopped = false;
276 for (int i = 0; i < numSlots + 1; i++) {
277 if (d->fillFullBlocks && (i == (stopSlot + 1))) {
278 stopped = true;
279 break;
280 }
281 p->fillRect(QRect(rect.left() + start, rect.top() + verticalSpacing, slotWidth, drawRect.height() - verticalSpacing * 2), fillInternalBar);
282 start += slotWidth + spacing;
283 }
284
285 if (!d->fillFullBlocks || (!stopped && (stopSlot != (numSlots + 1)) && (stopSlot != numSlots))) {
286 QPainterPath lastSlot;
287 lastSlot.moveTo(start, verticalSpacing);
288 lastSlot.lineTo(start, drawRect.height() - verticalSpacing);
289 lastSlot.lineTo(start + slotWidth + plusOffset, drawRect.height() - verticalSpacing);
290 lastSlot.quadTo(start + roundMargin, drawRect.height() / 2, start + slotWidth + plusOffset, verticalSpacing);
291 lastSlot.lineTo(start, verticalSpacing);
292 p->fillPath(lastSlot, fillInternalBar);
293 }
294 }
295 } else {
296 p->fillPath(internalBar, fillInternalBar);
297 }
298
299 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
300 p->restore();
301 }
302
303 p->save();
304 p->setClipping(false);
305 QRadialGradient topGradient(QPointF(rect.width() / 2, drawRect.top()), rect.width() / 2);
306 const QColor fillTopColor = palette().window().color().darker(LightShade);
307 topGradient.setColorAt(0, QColor(fillTopColor.red(), fillTopColor.green(), fillTopColor.blue(), 127));
308 topGradient.setColorAt(1, Qt::transparent);
309 p->fillRect(QRect(rect.left(), rect.top() + drawRect.top(), rect.width(), 2), topGradient);
310 p->restore();
311
312 p->save();
313 p->setClipRect(QRect(-1, 0, rect.width(), drawRect.height() / 2), Qt::ReplaceClip);
314 QLinearGradient glassGradient(0, -5, 0, drawRect.height());
315 const QColor fillGlassColor = palette().base().color();
316 glassGradient.setColorAt(0, QColor(fillGlassColor.red(), fillGlassColor.green(), fillGlassColor.blue(), 255));
317 glassGradient.setColorAt(1, Qt::transparent);
318 p->fillPath(internalBar, glassGradient);
319 p->restore();
320
321 p->restore();
322
323 if (d->drawTextMode == KCapacityBar::DrawTextInline) {
324 QRect rect(drawRect);
325 rect.setHeight(rect.height() + 4);
326 p->drawText(rect, Qt::AlignCenter, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width() - 2 * ROUND_MARGIN));
327 } else {
328 p->drawText(rect, Qt::AlignBottom | d->horizontalTextAlignment, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width()));
329 }
330}
331
332QSize KCapacityBar::minimumSizeHint() const
333{
334 int width = fontMetrics().boundingRect(d->text).width() + ((d->drawTextMode == KCapacityBar::DrawTextInline) ? ROUND_MARGIN * 2 : 0);
335
336 int height = (d->drawTextMode == KCapacityBar::DrawTextInline) ? qMax(fontMetrics().height(), d->barHeight)
337 : (d->text.isEmpty() ? 0 : fontMetrics().height() + VERTICAL_SPACING * 2) + d->barHeight;
338
339 if (height % 2) {
340 height++;
341 }
342
343 return QSize(width, height);
344}
345
346void KCapacityBar::paintEvent(QPaintEvent *event)
347{
348 QPainter p(this);
349 p.setClipRect(event->rect());
351 p.end();
352}
353
354void KCapacityBar::changeEvent(QEvent *event)
355{
357 if (event->type() == QEvent::StyleChange) {
358 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this);
359 }
360}
361
362#include "moc_kcapacitybar.cpp"
void setFillFullBlocks(bool fillFullBlocks)
When the capacity bar is non-continuous, sets whether the last block shown should be drawn full or ca...
void drawCapacityBar(QPainter *p, const QRect &rect) const
This method allows you to draw the widget, directly, for example on item delegates.
void setValue(int value)
Capacity bar fill value.
void setContinuous(bool continuous)
Sets whether the fill of the capacity bar should be continuous or in block mode.
void setHorizontalTextAlignment(Qt::Alignment textAlignment)
If the capacity bar is in outline text mode, draw the text with textAlignment alignment.
void setBarHeight(int barHeight)
Sets the height (in pixels) of the bar.
KCapacityBar(QWidget *parent=nullptr)
Constructs a capacity bar with DrawTextOutline as draw text mode.
void setDrawTextMode(DrawTextMode mode)
Set the way text is drawn if any is set.
void setText(const QString &text)
Sets the text for the capacity bar.
@ DrawTextOutline
If any text set, draw it out of the capacity bar.
@ DrawTextInline
If any text set, draw it into the capacity bar.
Q_SCRIPTABLE QString start(QString train="")
QStyle::ControlElement customControlElement(const QString &element, const QWidget *widget)
Resolve a dynamic QStyle::ControlElement for eg.
int blue() const const
QColor darker(int factor) const const
int green() const const
int red() const const
QRect boundingRect(QChar ch) const const
void setColorAt(qreal position, const QColor &color)
virtual bool event(QEvent *e)
QObject * parent() const const
void drawText(const QPoint &position, const QString &text)
void fillPath(const QPainterPath &path, const QBrush &brush)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
qreal opacity() const const
void restore()
void save()
void setBrush(Qt::BrushStyle style)
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setClipping(bool enable)
void setOpacity(qreal opacity)
void setPen(Qt::PenStyle style)
void setRenderHints(RenderHints hints, bool on)
void translate(const QPoint &offset)
void lineTo(const QPointF &endPoint)
void moveTo(const QPointF &point)
void quadTo(const QPointF &c, const QPointF &endPoint)
int bottom() const const
int height() const const
void setHeight(int height)
void setWidth(int width)
int top() const const
int width() const const
typedef State
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
void initFrom(const QWidget *widget)
typedef Alignment
IntersectClip
transparent
LeftToRight
ElideRight
QWidget(QWidget *parent, Qt::WindowFlags f)
void setAccessibleName(const QString &name)
virtual void changeEvent(QEvent *event)
QRect contentsRect() const const
QFontMetrics fontMetrics() const const
QStyle * style() const const
void update()
void updateGeometry()
QWidget * window() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:54:47 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.