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