2 * Copyright 2008 Long Huynh Huu <long.upcase@googlemail.com>
3 * Copyright 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
4 * Copyright 2007 Casper Boemann <cbr@boemann.dk>
5 * Copyright 2007 Fredrik Höglund <fredrik@kde.org>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License version 2 as published by the Free Software Foundation.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
24 #include <KGlobalSettings>
25 #include <KColorUtils>
26 #include <KColorScheme>
28 #include <QtGui/QWidget>
29 #include <QtGui/QPainter>
33 const double OxygenHelper::_shadowGain
= 1.5;
35 // NOTE: OxygenStyleHelper needs to use a KConfig from its own KComponentData
36 // Since the ctor order causes a SEGV if we try to pass in a KConfig here from
37 // a KComponentData constructed in the OxygenStyleHelper ctor, we'll just keep
38 // one here, even though the window decoration doesn't really need it.
39 OxygenHelper::OxygenHelper(const QByteArray
&componentName
)
40 : _componentData(componentName
, 0, KComponentData::SkipMainComponentRegistration
)
42 _config
= _componentData
.config();
43 _contrast
= KGlobalSettings::contrastF(_config
);
44 _bgcontrast
= 0.3;// // shouldn't use contrast for this _contrast; // TODO get style setting
46 m_backgroundCache
.setMaxCost(64);
47 m_windecoButtonCache
.setMaxCost(64);
48 m_windecoButtonGlowCache
.setMaxCost(64);
51 KSharedConfigPtr
OxygenHelper::config() const
56 void OxygenHelper::reloadConfig()
58 double old_contrast
= _contrast
;
60 _config
->reparseConfiguration();
61 _contrast
= KGlobalSettings::contrastF(_config
);
63 if (_contrast
!= old_contrast
)
64 invalidateCaches(); // contrast changed, invalidate our caches
67 void OxygenHelper::renderWindowBackground(QPainter
*p
, const QRect
&clipRect
, const QWidget
*widget
, const QPalette
& pal
, int y_shift
)
69 const QWidget
* window
= widget
->window();
70 // get coordinates relative to the client area
71 const QWidget
* w
= widget
;
72 int x
= 0, y
= -y_shift
;
73 while (!w
->isWindow()) {
74 x
+= w
->geometry().x();
75 y
+= w
->geometry().y();
76 w
= w
->parentWidget();
79 if (clipRect
.isValid()) {
81 p
->setClipRegion(clipRect
,Qt::IntersectClip
);
83 QRect r
= window
->rect();
84 QColor color
= pal
.color(window
->backgroundRole());
85 int splitY
= qMin(300, 3*r
.height()/4);
87 QRect upperRect
= QRect(-x
, -y
, r
.width(), splitY
);
88 QPixmap tile
= verticalGradient(color
, splitY
);
89 p
->drawTiledPixmap(upperRect
, tile
);
91 QRect lowerRect
= QRect(-x
, splitY
-y
, r
.width(), r
.height() - splitY
-y_shift
);
92 p
->fillRect(lowerRect
, backgroundBottomColor(color
));
94 int radialW
= qMin(600, r
.width());
95 int frameH
= 10; // on first paint the frame may not have been done yet, so just fixate it
96 QRect radialRect
= QRect((r
.width() - radialW
) / 2-x
, -y
, radialW
, 64-frameH
);
97 if (clipRect
.intersects(radialRect
))
99 tile
= radialGradient(color
, radialW
);
100 p
->drawPixmap(radialRect
, tile
);
103 if (clipRect
.isValid())
107 void OxygenHelper::invalidateCaches()
109 m_backgroundCache
.clear();
110 m_windecoButtonCache
.clear();
111 m_windecoButtonGlowCache
.clear();
114 bool OxygenHelper::lowThreshold(const QColor
&color
)
116 QColor darker
= KColorScheme::shade(color
, KColorScheme::MidShade
, 0.5);
117 return KColorUtils::luma(darker
) > KColorUtils::luma(color
);
120 QColor
OxygenHelper::alphaColor(QColor color
, double alpha
)
124 color
.setAlphaF(qMax(0.0, alpha
) * color
.alphaF());
128 QColor
OxygenHelper::backgroundRadialColor(const QColor
&color
) const
130 if (lowThreshold(color
))
131 return KColorScheme::shade(color
, KColorScheme::LightShade
, 0.0);
133 return KColorScheme::shade(color
, KColorScheme::LightShade
, _bgcontrast
);
136 QColor
OxygenHelper::backgroundTopColor(const QColor
&color
) const
138 if (lowThreshold(color
))
139 return KColorScheme::shade(color
, KColorScheme::MidlightShade
, 0.0);
141 return KColorScheme::shade(color
, KColorScheme::MidlightShade
, _bgcontrast
);
144 QColor
OxygenHelper::backgroundBottomColor(const QColor
&color
) const
146 QColor midColor
= KColorScheme::shade(color
, KColorScheme::MidShade
, 0.0);
147 if (lowThreshold(color
))
150 double by
= KColorUtils::luma(color
), my
= KColorUtils::luma(midColor
);
151 return KColorUtils::shade(color
, (my
- by
) * _bgcontrast
);
154 QColor
OxygenHelper::calcLightColor(const QColor
&color
) const
156 return KColorScheme::shade(color
, KColorScheme::LightShade
, _contrast
);
159 QColor
OxygenHelper::calcDarkColor(const QColor
&color
) const
161 if (lowThreshold(color
))
162 return KColorUtils::mix(calcLightColor(color
), color
, 0.2 + 0.8 * _contrast
);
164 return KColorScheme::shade(color
, KColorScheme::MidShade
, _contrast
);
167 QColor
OxygenHelper::calcShadowColor(const QColor
&color
) const
169 return KColorScheme::shade(KColorUtils::mix(QColor(255,255,255),color
, color
.alpha()*(1/255.0)),
170 KColorScheme::ShadowShade
, _contrast
);
174 QColor
OxygenHelper::backgroundColor(const QColor
&color
, int height
, int y
)
176 double h
= height
* 0.5;
178 double a
= double(y
) / h
;
179 return KColorUtils::mix(backgroundTopColor(color
), color
, a
);
182 double a
= (double(y
) - h
) / h
;
183 return KColorUtils::mix(color
, backgroundBottomColor(color
), a
);
187 QPixmap
OxygenHelper::verticalGradient(const QColor
&color
, int height
)
189 quint64 key
= (quint64(color
.rgba()) << 32) | height
| 0x8000;
190 QPixmap
*pixmap
= m_backgroundCache
.object(key
);
194 pixmap
= new QPixmap(32, height
);
196 QLinearGradient
gradient(0, 0, 0, height
);
197 gradient
.setColorAt(0.0, backgroundTopColor(color
));
198 gradient
.setColorAt(0.5, color
);
199 gradient
.setColorAt(1.0, backgroundBottomColor(color
));
202 p
.setCompositionMode(QPainter::CompositionMode_Source
);
203 p
.fillRect(pixmap
->rect(), gradient
);
205 m_backgroundCache
.insert(key
, pixmap
);
211 QPixmap
OxygenHelper::radialGradient(const QColor
&color
, int width
)
213 quint64 key
= (quint64(color
.rgba()) << 32) | width
| 0xb000;
214 QPixmap
*pixmap
= m_backgroundCache
.object(key
);
219 pixmap
= new QPixmap(width
, 64);
220 pixmap
->fill(QColor(0,0,0,0));
221 QColor radialColor
= backgroundRadialColor(color
);
222 radialColor
.setAlpha(255);
223 QRadialGradient
gradient(64, 0, 64);
224 gradient
.setColorAt(0, radialColor
);
225 radialColor
.setAlpha(101);
226 gradient
.setColorAt(0.5, radialColor
);
227 radialColor
.setAlpha(37);
228 gradient
.setColorAt(0.75, radialColor
);
229 radialColor
.setAlpha(0);
230 gradient
.setColorAt(1, radialColor
);
233 p
.scale(width
/128.0,1);
234 p
.fillRect(QRect(0,0,128,64), gradient
);
236 m_backgroundCache
.insert(key
, pixmap
);
242 void OxygenHelper::drawShadow(QPainter
&p
, const QColor
&color
, int size
) const
244 double m
= double(size
-2)*0.5;
246 const double offset
= 0.8;
247 double k0
= (m
-4.0) / m
;
248 QRadialGradient
shadowGradient(m
+1.0, m
+offset
+1.0, m
);
249 for (int i
= 0; i
< 8; i
++) { // sinusoidal gradient
250 double k1
= (k0
* double(8 - i
) + double(i
)) * 0.125;
251 double a
= (cos(3.14159 * i
* 0.125) + 1.0) * 0.25;
252 shadowGradient
.setColorAt(k1
, alphaColor(color
, a
* _shadowGain
));
254 shadowGradient
.setColorAt(1.0, alphaColor(color
, 0.0));
255 p
.setBrush(shadowGradient
);
256 p
.drawEllipse(QRectF(0, 0, size
, size
));
259 QLinearGradient
OxygenHelper::decoGradient(const QRect
&r
, const QColor
&color
)
261 QColor light
= KColorScheme::shade(color
, KColorScheme::LightShade
, _contrast
* 0.7);
262 QColor dark
= KColorScheme::shade(color
, KColorScheme::DarkShade
, _contrast
* 0.7);
263 double y
= KColorUtils::luma(color
);
264 double yd
= KColorUtils::luma(dark
);
265 double yl
= KColorUtils::luma(light
);
267 QLinearGradient
gradient(r
.topLeft(), r
.bottomLeft());
270 gradient
.setColorAt(0.2, color
);
271 gradient
.setColorAt(0.8, dark
);
275 gradient
.setColorAt(0.2, light
);
276 gradient
.setColorAt(0.8, color
);
280 gradient
.setColorAt(0.2, dark
);
281 gradient
.setColorAt(0.5, color
);
282 gradient
.setColorAt(0.8, light
);
288 QPixmap
OxygenHelper::windecoButton(const QColor
&color
, bool pressed
, int size
)
290 quint64 key
= (quint64(color
.rgba()) << 32) | (size
<< 1) | pressed
;
291 QPixmap
*pixmap
= m_windecoButtonCache
.object(key
);
295 pixmap
= new QPixmap(size
, size
);
296 pixmap
->fill(Qt::transparent
);
299 p
.setRenderHints(QPainter::Antialiasing
);
302 double u
= size
/21.0;
304 QColor light
= calcLightColor(color
);
305 QColor dark
= calcDarkColor(color
);
306 QColor shadow
= calcShadowColor(color
);
308 QRectF
rect(0.0, 0.0, size
, size
);
309 QRectF buttonRect
= rect
.adjusted(2*u
,2*u
,-2*u
,-2*u
);
311 QLinearGradient
fill(QPointF(0.0, 0.0*u
),
312 QPointF(0.0, 21.0*u
));
313 fill
.setColorAt(0.0, alphaColor(light
, 0.7));
314 fill
.setColorAt(1.0, alphaColor(dark
, 0.7));
317 p
.drawEllipse(buttonRect
);
318 p
.setBrush(Qt::NoBrush
);
323 p
.setPen(alphaColor(dark
, 0.4));
324 p
.drawEllipse(buttonRect
.adjusted(0.9, 0.6, -0.7, -0.7).adjusted(1.7,1.7,-1.7,-1.7));
325 p
.setPen(alphaColor(dark
, 0.8));
326 p
.drawEllipse(buttonRect
.adjusted(0.9, 0.6, -0.7, -0.7).adjusted(1.2,1.2,-1.2,-1.2));
328 p
.setPen(QPen(KColorUtils::mix(dark
, shadow
, 0.12), 2.0));
329 p
.drawEllipse(buttonRect
.adjusted(0.9, 0.6, -0.7, -0.7).adjusted(0,0.1,0,-0.1));
330 p
.setPen(QPen(KColorUtils::mix(dark
, shadow
, 0.6), 1.2));
331 p
.drawEllipse(buttonRect
.adjusted(1.0, 1.4, -0.8, -0.8));
334 QLinearGradient
lightgr(QPointF(0.0, 0.0*u
),
335 QPointF(0.0, 21.0*u
));
336 lightgr
.setColorAt(0.0, Qt::transparent
);
337 lightgr
.setColorAt(1.0, light
);
338 p
.setPen(QPen(lightgr
, 1.7));
339 p
.drawEllipse(buttonRect
.adjusted(0.0, -0.5, -0.1, 0.0));
341 m_windecoButtonCache
.insert(key
, pixmap
);
347 QPixmap
OxygenHelper::windecoButtonGlow(const QColor
&color
, int size
)
349 quint64 key
= (quint64(color
.rgba()) << 32) | size
;
350 QPixmap
*pixmap
= m_windecoButtonGlowCache
.object(key
);
354 pixmap
= new QPixmap(size
, size
);
355 pixmap
->fill(Qt::transparent
);
358 p
.setRenderHints(QPainter::Antialiasing
);
361 double u
= size
/21.0;
362 QRectF
rect(0.0, 0.0, size
, size
);
363 QRectF buttonRect
= rect
.adjusted(2*u
,2*u
,-2*u
,-2*u
);
366 p
.setBrush(QColor(0,0,0,125));
367 p
.drawEllipse(rect
.adjusted(u
, 0, -u
, -2*u
));
370 QColor dark
= calcDarkColor(color
);
371 QColor light
= calcLightColor(color
);
373 QRadialGradient
glow(QPointF(size
/2.0, size
/2.0 + 0.25), size
/2.0);
374 glow
.setColorAt(12.0 / 21.0, Qt::transparent
);
375 glow
.setColorAt(16.0 / 21.0, dark
);
376 glow
.setColorAt(18.0 / 21.0, alphaColor(light
, 0.25));
377 glow
.setColorAt(20.0 / 21.0, Qt::transparent
);
379 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
383 m_windecoButtonGlowCache
.insert(key
, pixmap
);
389 QPixmap
OxygenHelper::glow(const QColor
&color
, int size
, int rsize
)
391 QPixmap
pixmap(rsize
, rsize
);
392 pixmap
.fill(QColor(0,0,0,0));
395 p
.setRenderHints(QPainter::Antialiasing
);
397 p
.setWindow(0,0,size
,size
);
399 QRectF
r(0, 0, size
, size
);
400 double m
= double(size
)*0.5;
402 const double width
= 3.0;
403 const double bias
= _glowBias
* double(size
) / double(rsize
);
404 double k0
= (m
-width
+bias
) / m
;
405 QRadialGradient
glowGradient(m
, m
, m
);
406 for (int i
= 0; i
< 8; i
++) { // inverse parabolic gradient
407 double k1
= (k0
* double(8 - i
) + double(i
)) * 0.125;
408 double a
= 1.0 - sqrt(i
* 0.125);
409 glowGradient
.setColorAt(k1
, alphaColor(color
, a
));
411 glowGradient
.setColorAt(1.0, alphaColor(color
, 0.0));
414 p
.setBrush(glowGradient
);
418 p
.setCompositionMode(QPainter::CompositionMode_DestinationOut
);
419 p
.setBrush(QBrush(Qt::black
));
420 p
.drawEllipse(r
.adjusted(width
, width
, -width
, -width
));
427 void OxygenHelper::drawFloatFrame(QPainter
*p
, const QRect r
, const QColor
&color
, bool drawUglyShadow
, bool isActive
, const QColor
&frameColor
) const
429 p
->setRenderHint(QPainter::Antialiasing
);
431 frame
.adjust(1,1,-1,-1);
433 frame
.getRect(&x
, &y
, &w
, &h
);
435 QColor light
= calcLightColor(backgroundTopColor(color
));
436 QColor dark
= calcLightColor(backgroundBottomColor(color
));
437 QColor glow
= KColorUtils::mix(QColor(128,128,128),frameColor
,0.7);
439 p
->setBrush(Qt::NoBrush
);
441 if (drawUglyShadow
) {
442 if(isActive
) { //window active - it's a glow - not a shadow
444 p
->drawLine(QPointF(x
+4, y
-0.5), QPointF(x
+w
-4, y
-0.5));
445 p
->drawArc(QRectF(x
-0.5, y
-0.5, 11, 11),90*16, 90*16);
446 p
->drawArc(QRectF(x
+w
-11+0.5, y
-0.5, 11, 11), 0, 90*16);
447 p
->drawLine(QPointF(x
-0.5, y
+4), QPointF(x
-0.5, y
+h
-4));
448 p
->drawLine(QPointF(x
+w
+0.5, y
+4), QPointF(x
+w
+0.5, y
+h
-4));
449 p
->drawArc(QRectF(0.5, y
+h
-11+0.5, 11, 11),180*16, 90*16);
450 p
->drawArc(QRectF(x
+w
-11+0.5, y
+h
-11+0.5, 11, 11),270*16, 90*16);
451 p
->drawLine(QPointF(x
+4, y
+h
+0.5), QPointF(x
+w
-4, y
+h
+0.5));
452 light
= KColorUtils::mix(light
, frameColor
);
453 dark
= KColorUtils::mix(dark
, frameColor
);
455 else { //window inactive - draw something resembling shadow
456 QColor shadow
= KColorUtils::darken(color
, 0.0, 0.0); // fully desaturate
457 p
->setPen(KColorUtils::darken(shadow
, 0.2));
458 p
->drawLine(QPointF(x
+4, y
-0.5), QPointF(x
+w
-4, y
-0.5));
459 p
->drawArc(QRectF(x
-0.5, y
-0.5, 11, 11),90*16, 90*16);
460 p
->drawArc(QRectF(x
+w
-11+0.5, y
-0.5, 11, 11), 0, 90*16);
461 p
->setPen(KColorUtils::darken(shadow
, 0.35));
462 p
->drawLine(QPointF(x
-0.5, y
+4), QPointF(x
-0.5, y
+h
-4));
463 p
->drawLine(QPointF(x
+w
+0.5, y
+4), QPointF(x
+w
+0.5, y
+h
-4));
464 p
->setPen(KColorUtils::darken(shadow
, 0.45));
465 p
->drawArc(QRectF(0.5, y
+h
-11+0.5, 11, 11),180*16, 90*16);
466 p
->drawArc(QRectF(x
+w
-11+0.5, y
+h
-11+0.5, 11, 11),270*16, 90*16);
467 p
->setPen(KColorUtils::darken(shadow
, 0.6));
468 p
->drawLine(QPointF(x
+4, y
+h
+0.5), QPointF(x
+w
-4, y
+h
+0.5));
472 p
->setPen(QPen(light
, 0.8));
473 p
->drawLine(QPointF(x
+4, y
+0.6), QPointF(x
+w
-4, y
+0.6));
474 QLinearGradient lg
= QLinearGradient(0.0, 1.5, 0.0, 4.5);
475 lg
.setColorAt(0, light
);
476 lg
.setColorAt(1, dark
);
477 p
->setPen(QPen(lg
, 0.8));
478 p
->drawArc(QRectF(x
+0.6, y
+0.6, 9, 9),90*16, 90*16);
479 p
->drawArc(QRectF(x
+w
-9-0.6, y
+0.6, 9, 9), 0, 90*16);
480 p
->drawLine(QPointF(x
+0.6, y
+4), QPointF(x
+0.6, y
+h
-4));
481 p
->drawLine(QPointF(x
+w
-0.6, y
+4), QPointF(x
+w
-0.6, y
+h
-4));
486 void OxygenHelper::drawSeparator(QPainter
*p
, const QRect
&rect
, const QColor
&color
, Qt::Orientation orientation
) const
488 QColor light
= calcLightColor(color
);
489 QColor dark
= calcDarkColor(color
);
491 bool antialias
= p
->testRenderHint(QPainter::Antialiasing
);
492 p
->setRenderHint(QPainter::Antialiasing
,false);
494 QPoint start
,end
,offset
;
496 if (orientation
== Qt::Horizontal
) {
497 start
= QPoint(rect
.x(),rect
.y()+rect
.height()/2-1);
498 end
= QPoint(rect
.right(),rect
.y()+rect
.height()/2-1);
499 offset
= QPoint(0,1);
501 start
= QPoint(rect
.x()+rect
.width()/2-1,rect
.y());
502 end
= QPoint(rect
.x()+rect
.width()/2-1,rect
.bottom());
503 offset
= QPoint(1,0);
507 QLinearGradient
lg(start
,end
);
508 lg
.setColorAt(0.3, dark
);
509 lg
.setColorAt(0.7, dark
);
511 lg
.setColorAt(0.0, dark
);
512 lg
.setColorAt(1.0, dark
);
513 p
->setPen(QPen(lg
,1));
515 if (orientation
== Qt::Horizontal
)
516 p
->drawLine(start
,end
);
518 p
->drawLine(start
+offset
,end
+offset
);
520 lg
= QLinearGradient(start
,end
);
521 lg
.setColorAt(0.3, light
);
522 lg
.setColorAt(0.7, light
);
524 lg
.setColorAt(0.0, light
);
525 lg
.setColorAt(1.0, light
);
526 p
->setPen(QPen(lg
,1));
529 if (orientation
== Qt::Horizontal
)
530 p
->drawLine(start
+offset
,end
+offset
);
533 p
->drawLine(start
,end
);
534 p
->drawLine(start
+offset
*2,end
+offset
*2);
537 p
->setRenderHint(QPainter::Antialiasing
, antialias
);