File indexing completed on 2025-02-02 14:19:56
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kcapacitybar.h" 0009 #include "kstyleextensions.h" 0010 0011 #include <math.h> 0012 0013 #include <QLinearGradient> 0014 #include <QPaintEvent> 0015 #include <QPainter> 0016 #include <QPainterPath> 0017 #include <QStyle> 0018 #include <QStyleOptionProgressBar> 0019 0020 #define ROUND_MARGIN 6 0021 #define VERTICAL_SPACING 1 0022 0023 static const int LightShade = 100; 0024 static const int MidShade = 200; 0025 static const int DarkShade = 300; 0026 0027 class KCapacityBarPrivate 0028 { 0029 public: 0030 KCapacityBarPrivate(KCapacityBar::DrawTextMode drawTextMode) 0031 : drawTextMode(drawTextMode) 0032 { 0033 } 0034 0035 QString text; 0036 int value = 0; 0037 bool fillFullBlocks = true; 0038 bool continuous = true; 0039 int barHeight = 12; 0040 Qt::Alignment horizontalTextAlignment = Qt::AlignCenter; 0041 QStyle::ControlElement ce_capacityBar = QStyle::ControlElement(0); 0042 0043 KCapacityBar::DrawTextMode drawTextMode; 0044 }; 0045 0046 KCapacityBar::KCapacityBar(QWidget *parent) 0047 : KCapacityBar(DrawTextOutline, parent) 0048 { 0049 } 0050 0051 KCapacityBar::KCapacityBar(KCapacityBar::DrawTextMode drawTextMode, QWidget *parent) 0052 : QWidget(parent) 0053 , d(new KCapacityBarPrivate(drawTextMode)) 0054 { 0055 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this); 0056 } 0057 0058 KCapacityBar::~KCapacityBar() = default; 0059 0060 void KCapacityBar::setValue(int value) 0061 { 0062 d->value = value; 0063 update(); 0064 } 0065 0066 int KCapacityBar::value() const 0067 { 0068 return d->value; 0069 } 0070 0071 void KCapacityBar::setText(const QString &text) 0072 { 0073 bool updateGeom = d->text.isEmpty() || text.isEmpty(); 0074 d->text = text; 0075 if (updateGeom) { 0076 updateGeometry(); 0077 } 0078 0079 #ifndef QT_NO_ACCESSIBILITY 0080 setAccessibleName(text); 0081 #endif 0082 0083 update(); 0084 } 0085 0086 QString KCapacityBar::text() const 0087 { 0088 return d->text; 0089 } 0090 0091 void KCapacityBar::setFillFullBlocks(bool fillFullBlocks) 0092 { 0093 d->fillFullBlocks = fillFullBlocks; 0094 update(); 0095 } 0096 0097 bool KCapacityBar::fillFullBlocks() const 0098 { 0099 return d->fillFullBlocks; 0100 } 0101 0102 void KCapacityBar::setContinuous(bool continuous) 0103 { 0104 d->continuous = continuous; 0105 update(); 0106 } 0107 0108 bool KCapacityBar::continuous() const 0109 { 0110 return d->continuous; 0111 } 0112 0113 void KCapacityBar::setBarHeight(int barHeight) 0114 { 0115 // automatically convert odd values to even. This will make the bar look 0116 // better. 0117 d->barHeight = (barHeight % 2) ? barHeight + 1 : barHeight; 0118 updateGeometry(); 0119 } 0120 0121 int KCapacityBar::barHeight() const 0122 { 0123 return d->barHeight; 0124 } 0125 0126 void KCapacityBar::setHorizontalTextAlignment(Qt::Alignment horizontalTextAlignment) 0127 { 0128 Qt::Alignment alignment = horizontalTextAlignment; 0129 0130 // if the value came with any vertical alignment flag, remove it. 0131 alignment &= ~Qt::AlignTop; 0132 alignment &= ~Qt::AlignBottom; 0133 alignment &= ~Qt::AlignVCenter; 0134 0135 d->horizontalTextAlignment = alignment; 0136 update(); 0137 } 0138 0139 Qt::Alignment KCapacityBar::horizontalTextAlignment() const 0140 { 0141 return d->horizontalTextAlignment; 0142 } 0143 0144 void KCapacityBar::setDrawTextMode(DrawTextMode mode) 0145 { 0146 d->drawTextMode = mode; 0147 update(); 0148 } 0149 0150 KCapacityBar::DrawTextMode KCapacityBar::drawTextMode() const 0151 { 0152 return d->drawTextMode; 0153 } 0154 0155 void KCapacityBar::drawCapacityBar(QPainter *p, const QRect &rect) const 0156 { 0157 if (d->ce_capacityBar) { 0158 QStyleOptionProgressBar opt; 0159 opt.initFrom(this); 0160 opt.rect = rect; 0161 opt.minimum = 0; 0162 opt.maximum = 100; 0163 opt.progress = d->value; 0164 opt.state |= QStyle::State_Horizontal; 0165 opt.text = d->text; 0166 opt.textAlignment = Qt::AlignCenter; 0167 opt.textVisible = true; 0168 style()->drawControl(d->ce_capacityBar, &opt, p, this); 0169 0170 return; 0171 } 0172 0173 p->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); 0174 0175 p->save(); 0176 0177 QRect drawRect(rect); 0178 0179 if (d->drawTextMode == DrawTextOutline) { 0180 drawRect.setHeight(d->barHeight); 0181 } 0182 0183 QPainterPath outline; 0184 outline.moveTo(rect.left() + ROUND_MARGIN / 4 + 1, rect.top()); 0185 outline.lineTo(rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, rect.top()); 0186 outline.quadTo(rect.left() + drawRect.width() + ROUND_MARGIN / 2, 0187 drawRect.height() / 2 + rect.top(), 0188 rect.left() + drawRect.width() - ROUND_MARGIN / 4 - 1, 0189 drawRect.height() + rect.top()); 0190 outline.lineTo(rect.left() + ROUND_MARGIN / 4 + 1, drawRect.height() + rect.top()); 0191 outline.quadTo(-ROUND_MARGIN / 2 + rect.left(), drawRect.height() / 2 + rect.top(), rect.left() + ROUND_MARGIN / 4 + 1, rect.top()); 0192 const QColor fillColor = palette().window().color().darker(DarkShade); 0193 p->fillPath(outline, QColor(fillColor.red(), fillColor.green(), fillColor.blue(), 50)); 0194 0195 QRadialGradient bottomGradient(QPointF(rect.width() / 2, drawRect.bottom() + 1), rect.width() / 2); 0196 bottomGradient.setColorAt(0, palette().window().color().darker(LightShade)); 0197 bottomGradient.setColorAt(1, Qt::transparent); 0198 p->fillRect(QRect(rect.left(), drawRect.bottom() + rect.top(), rect.width(), 1), bottomGradient); 0199 0200 p->translate(rect.left() + 2, rect.top() + 1); 0201 0202 drawRect.setWidth(drawRect.width() - 4); 0203 drawRect.setHeight(drawRect.height() - 2); 0204 0205 QPainterPath path; 0206 path.moveTo(ROUND_MARGIN / 4, 0); 0207 path.lineTo(drawRect.width() - ROUND_MARGIN / 4, 0); 0208 path.quadTo(drawRect.width() + ROUND_MARGIN / 2, drawRect.height() / 2, drawRect.width() - ROUND_MARGIN / 4, drawRect.height()); 0209 path.lineTo(ROUND_MARGIN / 4, drawRect.height()); 0210 path.quadTo(-ROUND_MARGIN / 2, drawRect.height() / 2, ROUND_MARGIN / 4, 0); 0211 0212 QLinearGradient linearGradient(0, 0, 0, drawRect.height()); 0213 linearGradient.setColorAt(0.5, palette().window().color().darker(MidShade)); 0214 linearGradient.setColorAt(1, palette().window().color().darker(LightShade)); 0215 p->fillPath(path, linearGradient); 0216 0217 p->setBrush(Qt::NoBrush); 0218 p->setPen(Qt::NoPen); 0219 0220 if (d->continuous || !d->fillFullBlocks) { 0221 int start = (layoutDirection() == Qt::LeftToRight) ? -1 : (drawRect.width() + 2) - (drawRect.width() + 2) * (d->value / 100.0); 0222 0223 p->setClipRect(QRect(start, 0, (drawRect.width() + 2) * (d->value / 100.0), drawRect.height()), Qt::IntersectClip); 0224 } 0225 0226 int left = (layoutDirection() == Qt::LeftToRight) ? 0 : drawRect.width(); 0227 0228 int right = (layoutDirection() == Qt::LeftToRight) ? drawRect.width() : 0; 0229 0230 int roundMargin = (layoutDirection() == Qt::LeftToRight) ? ROUND_MARGIN : -ROUND_MARGIN; 0231 0232 int spacing = 2; 0233 int verticalSpacing = VERTICAL_SPACING; 0234 int slotWidth = 6; 0235 int start = roundMargin / 4; 0236 0237 QPainterPath internalBar; 0238 internalBar.moveTo(left + roundMargin / 4, 0); 0239 internalBar.lineTo(right - roundMargin / 4, 0); 0240 internalBar.quadTo(right + roundMargin / 2, drawRect.height() / 2, right - roundMargin / 4, drawRect.height()); 0241 internalBar.lineTo(left + roundMargin / 4, drawRect.height()); 0242 internalBar.quadTo(left - roundMargin / 2, drawRect.height() / 2, left + roundMargin / 4, 0); 0243 0244 QLinearGradient fillInternalBar(left, 0, right, 0); 0245 fillInternalBar.setColorAt(0, palette().window().color().darker(MidShade)); 0246 fillInternalBar.setColorAt(0.5, palette().window().color().darker(LightShade)); 0247 fillInternalBar.setColorAt(1, palette().window().color().darker(MidShade)); 0248 0249 if (d->drawTextMode == KCapacityBar::DrawTextInline) { 0250 p->save(); 0251 p->setOpacity(p->opacity() * 0.7); 0252 } 0253 0254 if (!d->continuous) { 0255 int numSlots = (drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) / (slotWidth + spacing); 0256 int stopSlot = floor((numSlots + 2) * (d->value / 100.0)); 0257 0258 int plusOffset = d->fillFullBlocks ? ((drawRect.width() - ROUND_MARGIN - ((slotWidth + spacing) * 2)) - (numSlots * (slotWidth + spacing))) / 2.0 : 0; 0259 0260 if (!d->fillFullBlocks || stopSlot) { 0261 QPainterPath firstSlot; 0262 firstSlot.moveTo(left + roundMargin / 4, verticalSpacing); 0263 firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, verticalSpacing); 0264 firstSlot.lineTo(left + slotWidth + roundMargin / 4 + plusOffset, drawRect.height() - verticalSpacing); 0265 firstSlot.lineTo(left + roundMargin / 4, drawRect.height() - verticalSpacing); 0266 firstSlot.quadTo(left, drawRect.height() / 2, left + roundMargin / 4, verticalSpacing); 0267 p->fillPath(firstSlot, fillInternalBar); 0268 start += slotWidth + spacing + plusOffset; 0269 0270 bool stopped = false; 0271 for (int i = 0; i < numSlots + 1; i++) { 0272 if (d->fillFullBlocks && (i == (stopSlot + 1))) { 0273 stopped = true; 0274 break; 0275 } 0276 p->fillRect(QRect(rect.left() + start, rect.top() + verticalSpacing, slotWidth, drawRect.height() - verticalSpacing * 2), fillInternalBar); 0277 start += slotWidth + spacing; 0278 } 0279 0280 if (!d->fillFullBlocks || (!stopped && (stopSlot != (numSlots + 1)) && (stopSlot != numSlots))) { 0281 QPainterPath lastSlot; 0282 lastSlot.moveTo(start, verticalSpacing); 0283 lastSlot.lineTo(start, drawRect.height() - verticalSpacing); 0284 lastSlot.lineTo(start + slotWidth + plusOffset, drawRect.height() - verticalSpacing); 0285 lastSlot.quadTo(start + roundMargin, drawRect.height() / 2, start + slotWidth + plusOffset, verticalSpacing); 0286 lastSlot.lineTo(start, verticalSpacing); 0287 p->fillPath(lastSlot, fillInternalBar); 0288 } 0289 } 0290 } else { 0291 p->fillPath(internalBar, fillInternalBar); 0292 } 0293 0294 if (d->drawTextMode == KCapacityBar::DrawTextInline) { 0295 p->restore(); 0296 } 0297 0298 p->save(); 0299 p->setClipping(false); 0300 QRadialGradient topGradient(QPointF(rect.width() / 2, drawRect.top()), rect.width() / 2); 0301 const QColor fillTopColor = palette().window().color().darker(LightShade); 0302 topGradient.setColorAt(0, QColor(fillTopColor.red(), fillTopColor.green(), fillTopColor.blue(), 127)); 0303 topGradient.setColorAt(1, Qt::transparent); 0304 p->fillRect(QRect(rect.left(), rect.top() + drawRect.top(), rect.width(), 2), topGradient); 0305 p->restore(); 0306 0307 p->save(); 0308 p->setClipRect(QRect(-1, 0, rect.width(), drawRect.height() / 2), Qt::ReplaceClip); 0309 QLinearGradient glassGradient(0, -5, 0, drawRect.height()); 0310 const QColor fillGlassColor = palette().base().color(); 0311 glassGradient.setColorAt(0, QColor(fillGlassColor.red(), fillGlassColor.green(), fillGlassColor.blue(), 255)); 0312 glassGradient.setColorAt(1, Qt::transparent); 0313 p->fillPath(internalBar, glassGradient); 0314 p->restore(); 0315 0316 p->restore(); 0317 0318 if (d->drawTextMode == KCapacityBar::DrawTextInline) { 0319 QRect rect(drawRect); 0320 rect.setHeight(rect.height() + 4); 0321 p->drawText(rect, Qt::AlignCenter, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width() - 2 * ROUND_MARGIN)); 0322 } else { 0323 p->drawText(rect, Qt::AlignBottom | d->horizontalTextAlignment, fontMetrics().elidedText(d->text, Qt::ElideRight, drawRect.width())); 0324 } 0325 } 0326 0327 QSize KCapacityBar::minimumSizeHint() const 0328 { 0329 int width = fontMetrics().boundingRect(d->text).width() + ((d->drawTextMode == KCapacityBar::DrawTextInline) ? ROUND_MARGIN * 2 : 0); 0330 0331 int height = (d->drawTextMode == KCapacityBar::DrawTextInline) ? qMax(fontMetrics().height(), d->barHeight) 0332 : (d->text.isEmpty() ? 0 : fontMetrics().height() + VERTICAL_SPACING * 2) + d->barHeight; 0333 0334 if (height % 2) { 0335 height++; 0336 } 0337 0338 return QSize(width, height); 0339 } 0340 0341 void KCapacityBar::paintEvent(QPaintEvent *event) 0342 { 0343 QPainter p(this); 0344 p.setClipRect(event->rect()); 0345 drawCapacityBar(&p, contentsRect()); 0346 p.end(); 0347 } 0348 0349 void KCapacityBar::changeEvent(QEvent *event) 0350 { 0351 QWidget::changeEvent(event); 0352 if (event->type() == QEvent::StyleChange) { 0353 d->ce_capacityBar = KStyleExtensions::customControlElement(QStringLiteral("CE_CapacityBar"), this); 0354 } 0355 } 0356 0357 #include "moc_kcapacitybar.cpp"