File indexing completed on 2024-05-12 09:37:34
0001 /* 0002 SPDX-FileCopyrightText: 2009-2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0003 SPDX-FileCopyrightText: 2008 Long Huynh Huu <long.upcase@googlemail.com> 0004 SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2007 Casper Boemann <cbr@boemann.dk> 0006 SPDX-FileCopyrightText: 2007 Fredrik H ?glund <fredrik@kde.org> 0007 0008 SPDX-License-Identifier: LGPL-2.0-only 0009 */ 0010 0011 #include "oxygenhelper.h" 0012 0013 #include <KColorScheme> 0014 #include <KColorUtils> 0015 #include <KWindowSystem> 0016 0017 #include <QApplication> 0018 #include <QPainter> 0019 #include <QTextStream> 0020 #include <math.h> 0021 0022 #if OXYGEN_HAVE_X11 0023 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0024 #include <QX11Info> 0025 #else 0026 #include <private/qtx11extras_p.h> 0027 #endif 0028 #endif 0029 0030 namespace Oxygen 0031 { 0032 0033 const qreal Helper::_slabThickness = 0.45; 0034 const qreal Helper::_shadowGain = 1.5; 0035 const qreal Helper::_glowBias = 0.6; 0036 0037 //____________________________________________________________________ 0038 Helper::Helper(KSharedConfig::Ptr config) 0039 : _config(config) 0040 { 0041 init(); 0042 } 0043 0044 //____________________________________________________________________ 0045 KSharedConfig::Ptr Helper::config() const 0046 { 0047 return _config; 0048 } 0049 0050 //____________________________________________________________________ 0051 void Helper::loadConfig() 0052 { 0053 _contrast = KColorScheme::contrastF(_config); 0054 0055 _bgcontrast = qMin(1.0, 0.9 * _contrast / 0.7); 0056 0057 _viewFocusBrush = KStatefulBrush(KColorScheme::View, KColorScheme::FocusColor, _config); 0058 _viewHoverBrush = KStatefulBrush(KColorScheme::View, KColorScheme::HoverColor, _config); 0059 _viewNegativeTextBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeText, _config); 0060 } 0061 0062 //____________________________________________________________________ 0063 void Helper::invalidateCaches() 0064 { 0065 _decoColorCache.clear(); 0066 _lightColorCache.clear(); 0067 _darkColorCache.clear(); 0068 _shadowColorCache.clear(); 0069 _backgroundTopColorCache.clear(); 0070 _backgroundBottomColorCache.clear(); 0071 _backgroundRadialColorCache.clear(); 0072 _backgroundColorCache.clear(); 0073 _backgroundCache.clear(); 0074 _dotCache.clear(); 0075 } 0076 0077 //____________________________________________________________________ 0078 void Helper::setMaxCacheSize(int value) 0079 { 0080 // assign value 0081 _backgroundCache.setMaxCost(value); 0082 _dotCache.setMaxCost(value); 0083 0084 /* note: we do not limit the size of the color caches on purpose, since they should be small anyway */ 0085 } 0086 0087 //____________________________________________________________________ 0088 void Helper::renderWindowBackground(QPainter *p, const QRect &clipRect, const QRect &windowRect, const QColor &color, int yShift) 0089 { 0090 if (clipRect.isValid()) { 0091 p->save(); 0092 p->setClipRegion(clipRect, Qt::IntersectClip); 0093 } 0094 0095 // draw upper linear gradient 0096 const int splitY(qMin(300, (3 * windowRect.height()) / 4)); 0097 0098 QRect upperRect = windowRect; 0099 if (splitY + yShift > 0) { 0100 upperRect.setHeight(splitY + yShift); 0101 QPixmap tile(verticalGradient(color, splitY + yShift, yShift)); 0102 p->drawTiledPixmap(upperRect, tile); 0103 } 0104 0105 // draw lower flat part 0106 const QRect lowerRect = windowRect.adjusted(0, splitY + yShift, 0, 0); 0107 if (lowerRect.isValid()) { 0108 p->fillRect(lowerRect, backgroundBottomColor(color)); 0109 } 0110 0111 // draw upper radial gradient 0112 const int radialW(qMin(600, windowRect.width())); 0113 const QRect radialRect((windowRect.width() - radialW) / 2 + windowRect.x(), windowRect.y(), radialW, 64 + yShift); 0114 if (clipRect.intersects(radialRect)) { 0115 QPixmap tile = radialGradient(color, radialW, 64 + yShift); 0116 p->drawPixmap(radialRect, tile); 0117 } 0118 0119 if (clipRect.isValid()) { 0120 p->restore(); 0121 } 0122 } 0123 0124 //____________________________________________________________________ 0125 void Helper::renderWindowBackground(QPainter *p, const QRect &clipRect, const QWidget *widget, const QWidget *window, const QColor &color, int yShift) 0126 { 0127 // get coordinates relative to the client area 0128 // this is stupid. One could use mapTo if this was taking const QWidget* and not 0129 // QWidget* as argument. 0130 const QWidget *w(widget); 0131 int x(0); 0132 int y(0); 0133 0134 while (w != window && !w->isWindow() && w != w->parentWidget()) { 0135 x += w->geometry().x(); 0136 y += w->geometry().y(); 0137 w = w->parentWidget(); 0138 } 0139 0140 // translate and call the base method 0141 const QRect r = window->rect().translated(-x, -y); 0142 renderWindowBackground(p, clipRect, r, color, yShift); 0143 } 0144 0145 //_____________________________________________________________ 0146 void Helper::renderDot(QPainter *p, const QPoint &point, const QColor &baseColor) 0147 { 0148 const quint64 key(colorKey(baseColor)); 0149 QPixmap pixmap; 0150 0151 if (QPixmap *cachedPixmap = _dotCache.object(key)) { 0152 pixmap = *cachedPixmap; 0153 0154 } else { 0155 pixmap = highDpiPixmap(4); 0156 pixmap.fill(Qt::transparent); 0157 const qreal diameter(1.8); 0158 0159 QPainter painter(&pixmap); 0160 painter.setRenderHint(QPainter::Antialiasing); 0161 painter.setPen(Qt::NoPen); 0162 0163 const QPoint center(QRect(0, 0, 4, 4).center()); 0164 0165 // light ellipse 0166 painter.setBrush(calcLightColor(baseColor)); 0167 painter.drawEllipse(QRectF(center.x() - diameter / 2 + 1.0, center.y() - diameter / 2 + 1.0, diameter, diameter)); 0168 0169 // dark ellipse 0170 painter.setBrush(calcDarkColor(baseColor).darker(130)); 0171 painter.drawEllipse(QRectF(center.x() - diameter / 2 + 0.5, center.y() - diameter / 2 + 0.5, diameter, diameter)); 0172 painter.end(); 0173 0174 // store in cache 0175 _dotCache.insert(key, new QPixmap(pixmap)); 0176 } 0177 0178 p->save(); 0179 p->translate(point - QPoint(1, 1)); 0180 p->setRenderHint(QPainter::Antialiasing); 0181 p->drawPixmap(QPoint(0, 0), pixmap); 0182 p->restore(); 0183 } 0184 0185 //____________________________________________________________________ 0186 bool Helper::lowThreshold(const QColor &color) 0187 { 0188 const quint32 key(colorKey(color)); 0189 ColorMap::iterator iter(_lowThreshold.find(key)); 0190 if (iter != _lowThreshold.end()) 0191 return iter.value(); 0192 else { 0193 const QColor darker(KColorScheme::shade(color, KColorScheme::MidShade, 0.5)); 0194 const bool result(KColorUtils::luma(darker) > KColorUtils::luma(color)); 0195 _lowThreshold.insert(key, result); 0196 return result; 0197 } 0198 } 0199 0200 //____________________________________________________________________ 0201 bool Helper::highThreshold(const QColor &color) 0202 { 0203 const quint32 key(colorKey(color)); 0204 ColorMap::iterator iter(_highThreshold.find(key)); 0205 if (iter != _highThreshold.end()) 0206 return iter.value(); 0207 else { 0208 const QColor lighter(KColorScheme::shade(color, KColorScheme::LightShade, 0.5)); 0209 const bool result(KColorUtils::luma(lighter) < KColorUtils::luma(color)); 0210 _highThreshold.insert(key, result); 0211 return result; 0212 } 0213 } 0214 0215 //____________________________________________________________________ 0216 QColor Helper::alphaColor(QColor color, qreal alpha) 0217 { 0218 if (alpha >= 0 && alpha < 1.0) { 0219 color.setAlphaF(alpha * color.alphaF()); 0220 } 0221 return color; 0222 } 0223 0224 //____________________________________________________________________ 0225 QColor Helper::backgroundRadialColor(const QColor &color) 0226 { 0227 const quint64 key(colorKey(color)); 0228 if (QColor *cachedColor = _backgroundRadialColorCache.object(key)) { 0229 return *cachedColor; 0230 } 0231 0232 QColor out; 0233 if (lowThreshold(color)) 0234 out = KColorScheme::shade(color, KColorScheme::LightShade, 0.0); 0235 else if (highThreshold(color)) 0236 out = color; 0237 else 0238 out = KColorScheme::shade(color, KColorScheme::LightShade, _bgcontrast); 0239 0240 _backgroundRadialColorCache.insert(key, new QColor(out)); 0241 0242 return out; 0243 } 0244 0245 //_________________________________________________________________________ 0246 QColor Helper::backgroundTopColor(const QColor &color) 0247 { 0248 const quint64 key(colorKey(color)); 0249 if (QColor *cachedColor = _backgroundTopColorCache.object(key)) { 0250 return *cachedColor; 0251 } 0252 0253 QColor out; 0254 if (lowThreshold(color)) 0255 out = KColorScheme::shade(color, KColorScheme::MidlightShade, 0.0); 0256 else { 0257 const qreal my(KColorUtils::luma(KColorScheme::shade(color, KColorScheme::LightShade, 0.0))); 0258 const qreal by(KColorUtils::luma(color)); 0259 out = KColorUtils::shade(color, (my - by) * _bgcontrast); 0260 } 0261 0262 _backgroundTopColorCache.insert(key, new QColor(out)); 0263 0264 return out; 0265 } 0266 0267 //_________________________________________________________________________ 0268 QColor Helper::backgroundBottomColor(const QColor &color) 0269 { 0270 const quint64 key(colorKey(color)); 0271 if (QColor *cachedColor = _backgroundBottomColorCache.object(key)) { 0272 return *cachedColor; 0273 } 0274 0275 QColor out; 0276 const QColor midColor(KColorScheme::shade(color, KColorScheme::MidShade, 0.0)); 0277 if (lowThreshold(color)) 0278 out = midColor; 0279 else { 0280 const qreal by(KColorUtils::luma(color)); 0281 const qreal my(KColorUtils::luma(midColor)); 0282 out = KColorUtils::shade(color, (my - by) * _bgcontrast); 0283 } 0284 0285 _backgroundBottomColorCache.insert(key, new QColor(out)); 0286 0287 return out; 0288 } 0289 0290 //____________________________________________________________________ 0291 QColor Helper::calcLightColor(const QColor &color) 0292 { 0293 const quint64 key(colorKey(color)); 0294 if (QColor *cachedColor = _lightColorCache.object(key)) { 0295 return *cachedColor; 0296 } 0297 0298 QColor out = highThreshold(color) ? color : KColorScheme::shade(color, KColorScheme::LightShade, _contrast); 0299 _lightColorCache.insert(key, new QColor(out)); 0300 0301 return out; 0302 } 0303 0304 //____________________________________________________________________ 0305 QColor Helper::calcDarkColor(const QColor &color) 0306 { 0307 const quint64 key(colorKey(color)); 0308 if (QColor *cachedColor = _darkColorCache.object(key)) { 0309 return *cachedColor; 0310 } 0311 0312 QColor out = (lowThreshold(color)) ? KColorUtils::mix(calcLightColor(color), color, 0.3 + 0.7 * _contrast) 0313 : KColorScheme::shade(color, KColorScheme::MidShade, _contrast); 0314 _darkColorCache.insert(key, new QColor(out)); 0315 0316 return out; 0317 } 0318 0319 //____________________________________________________________________ 0320 QColor Helper::calcShadowColor(const QColor &color) 0321 { 0322 const quint64 key(colorKey(color)); 0323 if (QColor *cachedColor = _shadowColorCache.object(key)) { 0324 return *cachedColor; 0325 } 0326 0327 QColor out = (lowThreshold(color)) ? KColorUtils::mix(Qt::black, color, color.alphaF()) 0328 : KColorScheme::shade(KColorUtils::mix(Qt::black, color, color.alphaF()), KColorScheme::ShadowShade, _contrast); 0329 0330 // make sure shadow color has the same alpha channel as the input 0331 out.setAlpha(color.alpha()); 0332 0333 // insert in cache 0334 _shadowColorCache.insert(key, new QColor(out)); 0335 0336 return out; 0337 } 0338 0339 //____________________________________________________________________ 0340 QColor Helper::backgroundColor(const QColor &color, qreal ratio) 0341 { 0342 const quint64 key((colorKey(color) << 32) | int(ratio * 512)); 0343 if (QColor *cachedColor = _backgroundColorCache.object(key)) { 0344 return *cachedColor; 0345 } 0346 0347 QColor out; 0348 if (ratio < 0.5) { 0349 const qreal a(2.0 * ratio); 0350 out = KColorUtils::mix(backgroundTopColor(color), color, a); 0351 0352 } else { 0353 const qreal a(2.0 * ratio - 1); 0354 out = KColorUtils::mix(color, backgroundBottomColor(color), a); 0355 } 0356 0357 _backgroundColorCache.insert(key, new QColor(out)); 0358 0359 return out; 0360 } 0361 0362 //____________________________________________________________________ 0363 QPixmap Helper::verticalGradient(const QColor &color, int height, int offset) 0364 { 0365 const quint64 key((colorKey(color) << 32) | height | 0x8000); 0366 0367 if (QPixmap *cachedPixmap = _backgroundCache.object(key)) { 0368 return *cachedPixmap; 0369 } 0370 0371 QPixmap pixmap(1, height); 0372 pixmap.fill(Qt::transparent); 0373 0374 QLinearGradient gradient(0, offset, 0, height); 0375 gradient.setColorAt(0.0, backgroundTopColor(color)); 0376 gradient.setColorAt(0.5, color); 0377 gradient.setColorAt(1.0, backgroundBottomColor(color)); 0378 0379 QPainter painter(&pixmap); 0380 painter.fillRect(pixmap.rect(), gradient); 0381 0382 painter.end(); 0383 0384 _backgroundCache.insert(key, new QPixmap(pixmap)); 0385 return pixmap; 0386 } 0387 0388 //____________________________________________________________________ 0389 QPixmap Helper::radialGradient(const QColor &color, int width, int height) 0390 { 0391 const quint64 key((colorKey(color) << 32) | width | 0xb000); 0392 0393 if (QPixmap *cachedPixmap = _backgroundCache.object(key)) { 0394 return *cachedPixmap; 0395 } 0396 0397 QPixmap pixmap(width, height); 0398 pixmap.fill(Qt::transparent); 0399 0400 QRadialGradient gradient(64, height - 64, 64); 0401 QColor radialColor = backgroundRadialColor(color); 0402 radialColor.setAlpha(255); 0403 gradient.setColorAt(0, radialColor); 0404 radialColor.setAlpha(101); 0405 gradient.setColorAt(0.5, radialColor); 0406 radialColor.setAlpha(37); 0407 gradient.setColorAt(0.75, radialColor); 0408 radialColor.setAlpha(0); 0409 gradient.setColorAt(1, radialColor); 0410 0411 QPainter painter(&pixmap); 0412 painter.setWindow(0, 0, 128, height); 0413 painter.fillRect(QRect(0, 0, 128, height), gradient); 0414 0415 painter.end(); 0416 0417 _backgroundCache.insert(key, new QPixmap(pixmap)); 0418 0419 return pixmap; 0420 } 0421 0422 //____________________________________________________________________________________ 0423 QColor Helper::decoColor(const QColor &background, const QColor &color) 0424 { 0425 const quint64 key((colorKey(background) << 32) | colorKey(color)); 0426 if (QColor *cachedColor = _decoColorCache.object(key)) { 0427 return *cachedColor; 0428 } 0429 0430 QColor out = KColorUtils::mix(background, color, 0.8 * (1.0 + _contrast)); 0431 _decoColorCache.insert(key, new QColor(out)); 0432 0433 return out; 0434 } 0435 0436 //_______________________________________________________________________ 0437 QRegion Helper::roundedMask(const QRect &rect, int left, int right, int top, int bottom) const 0438 { 0439 // get rect geometry 0440 int x, y, w, h; 0441 rect.getRect(&x, &y, &w, &h); 0442 0443 QRegion mask(x + 4 * left, y + 0 * top, w - 4 * (left + right), h - 0 * (top + bottom)); 0444 mask += QRegion(x + 0 * left, y + 4 * top, w - 0 * (left + right), h - 4 * (top + bottom)); 0445 mask += QRegion(x + 2 * left, y + 1 * top, w - 2 * (left + right), h - 1 * (top + bottom)); 0446 mask += QRegion(x + 1 * left, y + 2 * top, w - 1 * (left + right), h - 2 * (top + bottom)); 0447 0448 return mask; 0449 } 0450 0451 //______________________________________________________________________________ 0452 QBitmap Helper::roundedMask(const QSize &size, Corners corners, qreal radius) const 0453 { 0454 QBitmap bitmap(highDpiPixmap(size)); 0455 if (corners == 0) { 0456 bitmap.fill(Qt::color1); 0457 0458 } else { 0459 // initialize bitmap 0460 bitmap.fill(Qt::color0); 0461 0462 // setup painter 0463 QPainter painter(&bitmap); 0464 painter.setPen(Qt::NoPen); 0465 painter.setBrush(Qt::color1); 0466 0467 // get path 0468 const QPainterPath path(roundedPath(bitmap.rect(), corners, radius)); 0469 painter.drawPath(path); 0470 } 0471 0472 return bitmap; 0473 } 0474 0475 //______________________________________________________________________________ 0476 QPainterPath Helper::roundedPath(const QRect &rect, Corners corners, qreal radius) const 0477 { 0478 QPainterPath path; 0479 0480 // simple cases 0481 if (corners == 0) { 0482 path.addRect(rect); 0483 return path; 0484 } 0485 0486 if (corners == AllCorners) { 0487 path.addRoundedRect(rect, radius, radius); 0488 return path; 0489 } 0490 0491 const QSizeF cornerSize(2 * radius, 2 * radius); 0492 0493 // rotate counterclockwise 0494 // top left corner 0495 if (corners & CornerTopLeft) { 0496 path.moveTo(rect.topLeft() + QPointF(radius, 0)); 0497 path.arcTo(QRectF(rect.topLeft(), cornerSize), 90, 90); 0498 0499 } else 0500 path.moveTo(rect.topLeft()); 0501 0502 // bottom left corner 0503 if (corners & CornerBottomLeft) { 0504 path.lineTo(rect.bottomLeft() - QPointF(0, radius)); 0505 path.arcTo(QRectF(rect.bottomLeft() - QPointF(0, 2 * radius), cornerSize), 180, 90); 0506 0507 } else 0508 path.lineTo(rect.bottomLeft()); 0509 0510 // bottom right corner 0511 if (corners & CornerBottomRight) { 0512 path.lineTo(rect.bottomRight() - QPointF(radius, 0)); 0513 path.arcTo(QRectF(rect.bottomRight() - QPointF(2 * radius, 2 * radius), cornerSize), 270, 90); 0514 0515 } else 0516 path.lineTo(rect.bottomRight()); 0517 0518 // top right corner 0519 if (corners & CornerTopRight) { 0520 path.lineTo(rect.topRight() + QPointF(0, radius)); 0521 path.arcTo(QRectF(rect.topRight() - QPointF(2 * radius, 0), cornerSize), 0, 90); 0522 0523 } else 0524 path.lineTo(rect.topRight()); 0525 0526 path.closeSubpath(); 0527 return path; 0528 } 0529 0530 //______________________________________________________________________ 0531 void Helper::drawFloatFrame(QPainter *p, const QRect r, const QColor &color, bool drawUglyShadow, bool isActive, const QColor &frameColor, TileSet::Tiles tiles) 0532 { 0533 p->save(); 0534 p->setRenderHint(QPainter::Antialiasing); 0535 const QRect frame(r.adjusted(1, 1, -1, -1)); 0536 int x, y, w, h; 0537 frame.getRect(&x, &y, &w, &h); 0538 0539 QColor light(calcLightColor(backgroundTopColor(color))); 0540 QColor dark(calcLightColor(backgroundBottomColor(color))); 0541 p->setBrush(Qt::NoBrush); 0542 0543 if (drawUglyShadow) { 0544 if (isActive) { 0545 // window active - it's a glow - not a shadow 0546 const QColor glow(KColorUtils::mix(QColor(128, 128, 128), frameColor, 0.7)); 0547 p->setPen(glow); 0548 0549 if (tiles & TileSet::Top) { 0550 p->drawLine(QPointF(x + 4, y - 0.5), QPointF(x + w - 4, y - 0.5)); 0551 p->drawArc(QRectF(x - 0.5, y - 0.5, 11, 11), 90 * 16, 90 * 16); 0552 p->drawArc(QRectF(x + w - 11 + 0.5, y - 0.5, 11, 11), 0, 90 * 16); 0553 } 0554 0555 if (tiles & TileSet::Left) 0556 p->drawLine(QPointF(x - 0.5, y + 4), QPointF(x - 0.5, y + h - 4)); 0557 if (tiles & TileSet::Right) 0558 p->drawLine(QPointF(x + w + 0.5, y + 4), QPointF(x + w + 0.5, y + h - 4)); 0559 0560 if (tiles & TileSet::Bottom) { 0561 if (tiles & TileSet::Left) 0562 p->drawArc(QRectF(x - 0.5, y + h - 11 + 0.5, 11, 11), 180 * 16, 90 * 16); 0563 if (tiles & TileSet::Right) 0564 p->drawArc(QRectF(x + w - 11 + 0.5, y + h - 11 + 0.5, 11, 11), 270 * 16, 90 * 16); 0565 p->drawLine(QPointF(x + 4, y + h + 0.5), QPointF(x + w - 4, y + h + 0.5)); 0566 } 0567 0568 light = KColorUtils::mix(light, frameColor); 0569 dark = KColorUtils::mix(dark, frameColor); 0570 0571 } else { 0572 // window inactive - draw something resembling shadow 0573 // fully desaturate 0574 const QColor shadow(KColorUtils::darken(color, 0.0, 0.0)); 0575 0576 if (tiles & TileSet::Top) { 0577 p->setPen(KColorUtils::darken(shadow, 0.2)); 0578 p->drawLine(QPointF(x + 4, y - 0.5), QPointF(x + w - 4, y - 0.5)); 0579 if (tiles & TileSet::Left) 0580 p->drawArc(QRectF(x - 0.5, y - 0.5, 11, 11), 90 * 16, 90 * 16); 0581 if (tiles & TileSet::Right) 0582 p->drawArc(QRectF(x + w - 11 + 0.5, y - 0.5, 11, 11), 0, 90 * 16); 0583 } 0584 0585 p->setPen(KColorUtils::darken(shadow, 0.35)); 0586 if (tiles & TileSet::Left) 0587 p->drawLine(QPointF(x - 0.5, y + 4), QPointF(x - 0.5, y + h - 4)); 0588 if (tiles & TileSet::Right) 0589 p->drawLine(QPointF(x + w + 0.5, y + 4), QPointF(x + w + 0.5, y + h - 4)); 0590 0591 if (tiles & TileSet::Bottom) { 0592 p->setPen(KColorUtils::darken(shadow, 0.45)); 0593 if (tiles & TileSet::Left) 0594 p->drawArc(QRectF(x - 0.5, y + h - 11 + 0.5, 11, 11), 180 * 16, 90 * 16); 0595 if (tiles & TileSet::Right) 0596 p->drawArc(QRectF(x + w - 11 + 0.5, y + h - 11 + 0.5, 11, 11), 270 * 16, 90 * 16); 0597 p->setPen(KColorUtils::darken(shadow, 0.6)); 0598 p->drawLine(QPointF(x + 4, y + h + 0.5), QPointF(x + w - 4, y + h + 0.5)); 0599 } 0600 } 0601 } 0602 0603 // top frame 0604 if (tiles & TileSet::Top) { 0605 p->setPen(QPen(light, 0.8)); 0606 p->drawLine(QPointF(x + 4, y + 0.6), QPointF(x + w - 4, y + 0.6)); 0607 } 0608 0609 // corner and side frames 0610 // sides are drawn even if Top only is selected, but with a different gradient 0611 if (h >= 4 + 1.5) { 0612 QLinearGradient lg(0.0, y + 1.5, 0.0, y + h - 4); 0613 lg.setColorAt(0, light); 0614 lg.setColorAt(1, alphaColor(light, 0)); 0615 0616 if (h > 20.5) 0617 lg.setColorAt(qMax(0.0, 1.0 - 12.0 / (h - 5.5)), alphaColor(light, 0.5)); 0618 else if (h > 8.5) 0619 lg.setColorAt(qMax(0.0, 3.0 / (h - 5.5)), alphaColor(light, 0.5)); 0620 0621 p->setPen(QPen(lg, 0.8)); 0622 if (tiles & TileSet::Left) 0623 p->drawLine(QPointF(x + 0.6, y + 4), QPointF(x + 0.6, y + h - 4)); 0624 if (tiles & TileSet::Right) 0625 p->drawLine(QPointF(x + w - 0.6, y + 4), QPointF(x + w - 0.6, y + h - 4)); 0626 } 0627 0628 if (tiles & TileSet::Top) { 0629 const qreal offset = 0.5; 0630 const qreal arc(7.0); 0631 p->drawArc(QRectF(x + offset, y + offset, arc, arc), 90 * 16, 90 * 16); 0632 p->drawArc(QRectF(x + w - arc - offset, y + offset, arc, arc), 0, 90 * 16); 0633 } 0634 0635 p->restore(); 0636 } 0637 0638 //______________________________________________________________________________________ 0639 void Helper::drawSeparator(QPainter *p, const QRect &rect, const QColor &color, Qt::Orientation orientation) 0640 { 0641 QColor light(calcLightColor(color)); 0642 QColor dark(calcDarkColor(color)); 0643 0644 p->save(); 0645 p->setRenderHint(QPainter::Antialiasing, false); 0646 0647 QPoint start, end, offset; 0648 if (orientation == Qt::Horizontal) { 0649 start = QPoint(rect.x(), rect.y() + rect.height() / 2 - 1); 0650 end = QPoint(rect.right(), rect.y() + rect.height() / 2 - 1); 0651 offset = QPoint(0, 1); 0652 0653 } else { 0654 start = QPoint(rect.x() + rect.width() / 2 - 1, rect.y()); 0655 end = QPoint(rect.x() + rect.width() / 2 - 1, rect.bottom()); 0656 offset = QPoint(1, 0); 0657 light.setAlpha(150); 0658 } 0659 0660 QLinearGradient lg(start, end); 0661 lg.setColorAt(0.3, dark); 0662 lg.setColorAt(0.7, dark); 0663 dark.setAlpha(0); 0664 lg.setColorAt(0.0, dark); 0665 lg.setColorAt(1.0, dark); 0666 p->setPen(QPen(lg, 1)); 0667 0668 if (orientation == Qt::Horizontal) 0669 p->drawLine(start, end); 0670 else 0671 p->drawLine(start + offset, end + offset); 0672 0673 lg = QLinearGradient(start, end); 0674 lg.setColorAt(0.3, light); 0675 lg.setColorAt(0.7, light); 0676 light.setAlpha(0); 0677 lg.setColorAt(0.0, light); 0678 lg.setColorAt(1.0, light); 0679 p->setPen(QPen(lg, 1)); 0680 0681 if (orientation == Qt::Horizontal) 0682 p->drawLine(start + offset, end + offset); 0683 else { 0684 p->drawLine(start, end); 0685 p->drawLine(start + offset * 2, end + offset * 2); 0686 } 0687 0688 p->restore(); 0689 } 0690 0691 //____________________________________________________________________ 0692 const QWidget *Helper::checkAutoFillBackground(const QWidget *w) const 0693 { 0694 if (!w) 0695 return nullptr; 0696 if (w->autoFillBackground()) 0697 return w; 0698 if (w->isWindow()) 0699 return nullptr; 0700 0701 for (const QWidget *parent = w->parentWidget(); parent != nullptr; parent = parent->parentWidget()) { 0702 if (parent->autoFillBackground()) 0703 return parent; 0704 if (parent == w->window()) 0705 break; 0706 } 0707 0708 return nullptr; 0709 } 0710 0711 //____________________________________________________________________ 0712 void Helper::setHasBackgroundGradient(WId id, bool value) const 0713 { 0714 #if OXYGEN_HAVE_X11 0715 setHasHint(id, _backgroundGradientAtom, value); 0716 #else 0717 Q_UNUSED(id); 0718 Q_UNUSED(value); 0719 #endif 0720 return; 0721 } 0722 0723 //____________________________________________________________________ 0724 bool Helper::hasBackgroundGradient(WId id) const 0725 { 0726 #if OXYGEN_HAVE_X11 0727 return hasHint(id, _backgroundGradientAtom); 0728 #else 0729 Q_UNUSED(id); 0730 return false; 0731 #endif 0732 } 0733 0734 //______________________________________________________________________________________ 0735 QPixmap Helper::highDpiPixmap(int width, int height) const 0736 { 0737 const qreal dpiRatio(qApp->devicePixelRatio()); 0738 QPixmap pixmap(width * dpiRatio, height * dpiRatio); 0739 pixmap.setDevicePixelRatio(dpiRatio); 0740 return pixmap; 0741 } 0742 0743 //______________________________________________________________________________________ 0744 qreal Helper::devicePixelRatio(const QPixmap &pixmap) const 0745 { 0746 return pixmap.devicePixelRatio(); 0747 } 0748 0749 //______________________________________________________________________________ 0750 bool Helper::isX11(void) 0751 { 0752 #if OXYGEN_HAVE_X11 0753 static const bool s_isX11 = KWindowSystem::isPlatformX11(); 0754 return s_isX11; 0755 #endif 0756 0757 return false; 0758 } 0759 0760 #if OXYGEN_HAVE_X11 0761 0762 //____________________________________________________________________ 0763 xcb_connection_t *Helper::connection(void) 0764 { 0765 return QX11Info::connection(); 0766 } 0767 0768 //____________________________________________________________________ 0769 xcb_atom_t Helper::createAtom(const QString &name) const 0770 { 0771 if (!isX11()) 0772 return 0; 0773 xcb_intern_atom_cookie_t cookie(xcb_intern_atom(connection(), false, name.size(), qPrintable(name))); 0774 ScopedPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(connection(), cookie, nullptr)); 0775 return reply ? reply->atom : 0; 0776 } 0777 0778 #endif 0779 0780 //___________________________________________________________________________________________ 0781 void Helper::drawShadow(QPainter &painter, const QColor &color, int size) 0782 { 0783 const qreal m(qreal(size - 2) * 0.5); 0784 const qreal offset(0.8); 0785 const qreal k0((m - 4.0) / m); 0786 0787 QRadialGradient shadowGradient(m + 1.0, m + offset + 1.0, m); 0788 for (int i = 0; i < 8; i++) { 0789 // sinusoidal gradient 0790 const qreal k1((k0 * qreal(8 - i) + qreal(i)) * 0.125); 0791 const qreal a((cos(M_PI * i * 0.125) + 1.0) * 0.30); 0792 shadowGradient.setColorAt(k1, alphaColor(color, a * _shadowGain)); 0793 } 0794 0795 shadowGradient.setColorAt(1.0, alphaColor(color, 0.0)); 0796 painter.save(); 0797 painter.setBrush(shadowGradient); 0798 painter.drawEllipse(QRectF(0, 0, size, size)); 0799 painter.restore(); 0800 } 0801 0802 //_______________________________________________________________________ 0803 void Helper::drawOuterGlow(QPainter &painter, const QColor &color, int size) 0804 { 0805 const QRectF r(0, 0, size, size); 0806 const qreal m(qreal(size) * 0.5); 0807 const qreal width(3); 0808 0809 const qreal bias(_glowBias * qreal(14) / size); 0810 0811 // k0 is located at width - bias from the outer edge 0812 const qreal gm(m + bias - 0.9); 0813 const qreal k0((m - width + bias) / gm); 0814 QRadialGradient glowGradient(m, m, gm); 0815 for (int i = 0; i < 8; i++) { 0816 // k1 grows linearly from k0 to 1.0 0817 const qreal k1(k0 + qreal(i) * (1.0 - k0) / 8.0); 0818 0819 // a folows sqrt curve 0820 const qreal a(1.0 - sqrt(qreal(i) / 8)); 0821 glowGradient.setColorAt(k1, alphaColor(color, a)); 0822 } 0823 0824 // glow 0825 painter.save(); 0826 painter.setBrush(glowGradient); 0827 painter.drawEllipse(r); 0828 0829 // inside mask 0830 painter.setCompositionMode(QPainter::CompositionMode_DestinationOut); 0831 painter.setBrush(Qt::black); 0832 painter.drawEllipse(r.adjusted(width + 0.5, width + 0.5, -width - 1, -width - 1)); 0833 painter.restore(); 0834 } 0835 0836 #if OXYGEN_HAVE_X11 0837 0838 //____________________________________________________________________ 0839 void Helper::setHasHint(xcb_window_t id, xcb_atom_t atom, bool value) const 0840 { 0841 if (!isX11()) 0842 return; 0843 0844 // check window id 0845 if (!id) 0846 return; 0847 0848 quint32 uLongValue(value); 0849 xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, id, atom, XCB_ATOM_CARDINAL, 32, 1, &uLongValue); 0850 xcb_flush(connection()); 0851 return; 0852 } 0853 0854 //____________________________________________________________________ 0855 bool Helper::hasHint(xcb_window_t id, xcb_atom_t atom) const 0856 { 0857 if (!isX11()) 0858 return false; 0859 0860 // check window id 0861 if (!id) 0862 return false; 0863 0864 xcb_get_property_cookie_t cookie(xcb_get_property(connection(), 0, id, atom, XCB_ATOM_CARDINAL, 0, 1)); 0865 ScopedPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(connection(), cookie, nullptr)); 0866 0867 return reply && xcb_get_property_value_length(reply.data()) && reinterpret_cast<int32_t *>(xcb_get_property_value(reply.data()))[0]; 0868 } 0869 0870 #endif 0871 0872 //____________________________________________________________________ 0873 void Helper::init(void) 0874 { 0875 _contrast = KColorScheme::contrastF(_config); 0876 0877 // background contrast is calculated so that it is 0.9 0878 // when KGlobalSettings contrast value of 0.7 0879 _bgcontrast = qMin(1.0, 0.9 * _contrast / 0.7); 0880 0881 _backgroundCache.setMaxCost(64); 0882 0883 #if OXYGEN_HAVE_X11 0884 if (isX11()) { 0885 _backgroundGradientAtom = createAtom(QStringLiteral("_KDE_OXYGEN_BACKGROUND_GRADIENT")); 0886 0887 } else { 0888 _backgroundGradientAtom = 0; 0889 } 0890 0891 #endif 0892 } 0893 }