Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / runtime / kstyles / oxygen / lib / helper.cpp
blob83881a91f730009d3c712aa244f46251b0071295
1 /*
2 * Copyright 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
3 * Copyright 2007 Casper Boemann <cbr@boemann.dk>
4 * Copyright 2007 Fredrik Höglund <fredrik@kde.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License version 2 as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
21 #include "helper.h"
23 #include <KGlobalSettings>
24 #include <KColorUtils>
25 #include <KColorScheme>
27 #include <QtGui/QPainter>
29 #include <math.h>
31 const double OxygenHelper::_shadowGain = 1.5;
33 // NOTE: OxygenStyleHelper needs to use a KConfig from its own KComponentData
34 // Since the ctor order causes a SEGV if we try to pass in a KConfig here from
35 // a KComponentData constructed in the OxygenStyleHelper ctor, we'll just keep
36 // one here, even though the window decoration doesn't really need it.
37 OxygenHelper::OxygenHelper(const QByteArray &componentName)
38 : _componentData(componentName, 0, KComponentData::SkipMainComponentRegistration)
40 _config = _componentData.config();
41 _contrast = KGlobalSettings::contrastF(_config);
42 _bgcontrast = 0.3;// // shouldn't use contrast for this _contrast; // TODO get style setting
44 m_backgroundCache.setMaxCost(64);
45 m_windecoButtonCache.setMaxCost(64);
48 KSharedConfigPtr OxygenHelper::config() const
50 return _config;
53 void OxygenHelper::reloadConfig()
55 double old_contrast = _contrast;
57 _config->reparseConfiguration();
58 _contrast = KGlobalSettings::contrastF(_config);
60 if (_contrast != old_contrast)
61 invalidateCaches(); // contrast changed, invalidate our caches
64 void OxygenHelper::invalidateCaches()
66 m_backgroundCache.clear();
67 m_windecoButtonCache.clear();
70 bool OxygenHelper::lowThreshold(const QColor &color)
72 QColor darker = KColorScheme::shade(color, KColorScheme::MidShade, 0.5);
73 return KColorUtils::luma(darker) > KColorUtils::luma(color);
76 QColor OxygenHelper::alphaColor(QColor color, double alpha)
78 if (alpha >= 1.0)
79 return color;
80 color.setAlphaF(qMax(0.0, alpha) * color.alphaF());
81 return color;
84 QColor OxygenHelper::backgroundRadialColor(const QColor &color) const
86 if (lowThreshold(color))
87 return KColorScheme::shade(color, KColorScheme::LightShade, 0.0);
88 else
89 return KColorScheme::shade(color, KColorScheme::LightShade, _bgcontrast);
92 QColor OxygenHelper::backgroundTopColor(const QColor &color) const
94 if (lowThreshold(color))
95 return KColorScheme::shade(color, KColorScheme::MidlightShade, 0.0);
96 else
97 return KColorScheme::shade(color, KColorScheme::MidlightShade, _bgcontrast);
100 QColor OxygenHelper::backgroundBottomColor(const QColor &color) const
102 QColor midColor = KColorScheme::shade(color, KColorScheme::MidShade, 0.0);
103 if (lowThreshold(color))
104 return midColor;
106 double by = KColorUtils::luma(color), my = KColorUtils::luma(midColor);
107 return KColorUtils::shade(color, (my - by) * _bgcontrast);
110 QColor OxygenHelper::calcLightColor(const QColor &color) const
112 return KColorScheme::shade(color, KColorScheme::LightShade, _contrast);
115 QColor OxygenHelper::calcDarkColor(const QColor &color) const
117 if (lowThreshold(color))
118 return KColorUtils::mix(calcLightColor(color), color, 0.2 + 0.8 * _contrast);
119 else
120 return KColorScheme::shade(color, KColorScheme::MidShade, _contrast);
123 QColor OxygenHelper::calcShadowColor(const QColor &color) const
125 return KColorScheme::shade(color, KColorScheme::ShadowShade, _contrast);
128 QColor OxygenHelper::backgroundColor(const QColor &color, int height, int y)
130 double h = height * 0.5;
131 if (y > height>>1) {
132 double a = double(y) / h;
133 return KColorUtils::mix(backgroundTopColor(color), color, a);
135 else {
136 double a = (double(y) - h) / h;
137 return KColorUtils::mix(color, backgroundBottomColor(color), a);
141 QPixmap OxygenHelper::verticalGradient(const QColor &color, int height)
143 quint64 key = (quint64(color.rgba()) << 32) | height | 0x8000;
144 QPixmap *pixmap = m_backgroundCache.object(key);
146 if (!pixmap)
148 pixmap = new QPixmap(32, height);
150 QLinearGradient gradient(0, 0, 0, height);
151 gradient.setColorAt(0.0, backgroundTopColor(color));
152 gradient.setColorAt(0.5, color);
153 gradient.setColorAt(1.0, backgroundBottomColor(color));
155 QPainter p(pixmap);
156 p.setCompositionMode(QPainter::CompositionMode_Source);
157 p.fillRect(pixmap->rect(), gradient);
159 m_backgroundCache.insert(key, pixmap);
162 return *pixmap;
165 QPixmap OxygenHelper::radialGradient(const QColor &color, int width)
167 quint64 key = (quint64(color.rgba()) << 32) | width | 0xb000;
168 QPixmap *pixmap = m_backgroundCache.object(key);
170 if (!pixmap)
172 // width /= 2;
173 pixmap = new QPixmap(width, 64);
174 pixmap->fill(QColor(0,0,0,0));
175 QColor radialColor = backgroundRadialColor(color);
176 radialColor.setAlpha(255);
177 QRadialGradient gradient(64, 0, 64);
178 gradient.setColorAt(0, radialColor);
179 radialColor.setAlpha(101);
180 gradient.setColorAt(0.5, radialColor);
181 radialColor.setAlpha(37);
182 gradient.setColorAt(0.75, radialColor);
183 radialColor.setAlpha(0);
184 gradient.setColorAt(1, radialColor);
186 QPainter p(pixmap);
187 p.scale(width/128.0,1);
188 p.fillRect(QRect(0,0,128,64), gradient);
190 m_backgroundCache.insert(key, pixmap);
193 return *pixmap;
196 void OxygenHelper::drawShadow(QPainter &p, const QColor &color, int size) const
198 double m = double(size-2)*0.5;
200 const double offset = 0.8;
201 double k0 = (m-4.0) / m;
202 QRadialGradient shadowGradient(m+1.0, m+offset+1.0, m);
203 for (int i = 0; i < 8; i++) { // sinusoidal gradient
204 double k1 = (k0 * double(8 - i) + double(i)) * 0.125;
205 double a = (cos(3.14159 * i * 0.125) + 1.0) * 0.25;
206 shadowGradient.setColorAt(k1, alphaColor(color, a * _shadowGain));
208 shadowGradient.setColorAt(1.0, alphaColor(color, 0.0));
209 p.setBrush(shadowGradient);
210 p.drawEllipse(QRectF(0, 0, size, size));
213 QLinearGradient OxygenHelper::decoGradient(const QRect &r, const QColor &color)
215 QColor light = KColorScheme::shade(color, KColorScheme::LightShade, _contrast * 0.7);
216 QColor dark = KColorScheme::shade(color, KColorScheme::DarkShade, _contrast * 0.7);
217 double y = KColorUtils::luma(color);
218 double yd = KColorUtils::luma(dark);
219 double yl = KColorUtils::luma(light);
221 QLinearGradient gradient(r.topLeft(), r.bottomLeft());
222 if (yd > y)
224 gradient.setColorAt(0.2, color);
225 gradient.setColorAt(0.8, dark);
227 else if (yl < y)
229 gradient.setColorAt(0.2, light);
230 gradient.setColorAt(0.8, color);
232 else
234 gradient.setColorAt(0.2, dark);
235 gradient.setColorAt(0.5, color);
236 gradient.setColorAt(0.8, light);
239 return gradient;
242 QPixmap OxygenHelper::windecoButton(const QColor &color, int size)
244 quint64 key = (quint64(color.rgba()) << 32) | size;
245 QPixmap *pixmap = m_windecoButtonCache.object(key);
247 if (!pixmap)
249 pixmap = new QPixmap(size*3, size*3);
250 pixmap->fill(QColor(0,0,0,0));
252 QPainter p(pixmap);
253 p.setRenderHints(QPainter::Antialiasing);
254 p.setPen(Qt::NoPen);
255 p.setWindow(0,0,21,21);
257 QColor light = calcLightColor(color);
258 QColor dark = calcDarkColor(color);
260 // shadow
261 drawShadow(p, calcShadowColor(color), 21);
263 // bevel
264 qreal y = KColorUtils::luma(color);
265 qreal yl = KColorUtils::luma(light);
266 qreal yd = KColorUtils::luma(dark);
267 QLinearGradient bevelGradient(0, 1, 0, 20);
268 bevelGradient.setColorAt(0.45, light);
269 bevelGradient.setColorAt(0.80, dark);
270 if (y < yl && y > yd) // no middle when color is very light/dark
271 bevelGradient.setColorAt(0.55, color);
272 p.setBrush(QBrush(bevelGradient));
273 p.drawEllipse(QRectF(3.0,3.0,15.0,15.0));
275 // inside mask
276 QRadialGradient maskGradient(10.5,10.5,7.5);
277 maskGradient.setColorAt(0.70, QColor(0,0,0,0));
278 maskGradient.setColorAt(0.85, QColor(0,0,0,140));
279 maskGradient.setColorAt(0.95, QColor(0,0,0,255));
280 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
281 p.setBrush(maskGradient);
282 p.drawRect(0,0,21,21);
284 // inside
285 QLinearGradient innerGradient(0, 4, 0, 17);
286 innerGradient.setColorAt(0.0, color);
287 innerGradient.setColorAt(1.0, light);
288 p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
289 p.setBrush(innerGradient);
290 p.drawEllipse(QRectF(3.0,3.0,15.0,15.0));
292 // anti-shadow
293 QRadialGradient highlightGradient(10.5,10,8.5);
294 highlightGradient.setColorAt(0.85, alphaColor(light, 0.0));
295 highlightGradient.setColorAt(1.00, light);
296 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
297 p.setBrush(highlightGradient);
298 p.drawEllipse(QRectF(3.0,3.0,15.0,15.0));
300 m_windecoButtonCache.insert(key, pixmap);
303 return *pixmap;
306 QPixmap OxygenHelper::glow(const QColor &color, int size, int rsize)
308 QPixmap pixmap(rsize, rsize);
309 pixmap.fill(QColor(0,0,0,0));
311 QPainter p(&pixmap);
312 p.setRenderHints(QPainter::Antialiasing);
313 p.setPen(Qt::NoPen);
314 p.setWindow(0,0,size,size);
316 QRectF r(0, 0, size, size);
317 double m = double(size)*0.5;
319 const double width = 3.0;
320 const double bias = _glowBias * double(size) / double(rsize);
321 double k0 = (m-width+bias) / m;
322 QRadialGradient glowGradient(m, m, m);
323 for (int i = 0; i < 8; i++) { // inverse parabolic gradient
324 double k1 = (k0 * double(8 - i) + double(i)) * 0.125;
325 double a = 1.0 - sqrt(i * 0.125);
326 glowGradient.setColorAt(k1, alphaColor(color, a));
328 glowGradient.setColorAt(1.0, alphaColor(color, 0.0));
330 // glow
331 p.setBrush(glowGradient);
332 p.drawEllipse(r);
334 // mask
335 p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
336 p.setBrush(QBrush(Qt::black));
337 p.drawEllipse(r.adjusted(width, width, -width, -width));
339 p.end();
341 return pixmap;
344 QPixmap OxygenHelper::windecoButtonFocused(const QColor &color, const QColor &glowColor, int size)
346 quint64 key = (quint64(glowColor.rgba()) << 32) | size;
347 QPixmap *pixmap = m_windecoButtonCache.object(key);
349 if (!pixmap)
351 pixmap = new QPixmap(size*3, size*3);
352 pixmap->fill(QColor(0,0,0,0));
354 QPainter p(pixmap);
355 p.setRenderHints(QPainter::Antialiasing);
356 p.setPen(Qt::NoPen);
357 p.setWindow(0,0,21,21);
359 // slab
360 QPixmap slabPixmap = windecoButton(color, size);
361 p.drawPixmap(0, 0, slabPixmap);
363 // glow
364 QPixmap gp = glow(glowColor, 21, size*3);
365 p.drawPixmap(0, 0, gp);
367 p.end();
369 m_windecoButtonCache.insert(key, pixmap);
371 return *pixmap;
374 void OxygenHelper::drawFloatFrame(QPainter *p, const QRect r, const QColor &color) const
376 p->setRenderHint(QPainter::Antialiasing);
377 QRect frame = r;
378 frame.adjust(1,1,-1,-1);
379 int x,y,w,h;
380 frame.getRect(&x, &y, &w, &h);
382 QColor light = calcLightColor(backgroundTopColor(color));
383 QColor dark = calcDarkColor(color);
385 p->setBrush(Qt::NoBrush);
387 if (0) { // TODO make option
388 QColor shadow = calcShadowColor(color); // wrong, use kwin shadow color
389 p->setPen(alphaColor(shadow, 0.1));
390 p->drawLine(QPointF(x+4, y-0.5), QPointF(x+w-4, y-0.5));
391 p->drawArc(QRectF(x-0.5, y-0.5, 11, 11),90*16, 90*16);
392 p->drawArc(QRectF(x+w-11+0.5, y-0.5, 11, 11), 0, 90*16);
393 p->setPen(alphaColor(shadow, 0.3));
394 p->drawLine(QPointF(x-0.5, y+4), QPointF(x-0.5, y+h));
395 p->drawLine(QPointF(x+w+0.5, y+4), QPointF(x+w+0.5, y+h));
396 p->setPen(alphaColor(shadow, 0.4));
397 p->drawArc(QRectF(0.5, y+h-11+0.5, 11, 11),180*16, 90*16);
398 p->drawArc(QRectF(x+w-11+0.5, y+h-11+0.5, 11, 11),270*16, 90*16);
399 p->setPen(alphaColor(shadow, 0.55));
400 p->drawLine(QPointF(x+4, y+h+0.5), QPointF(x+w-4, y+h+0.5));
402 else if (1) { // TODO make option
403 QColor shadow = KColorUtils::darken(color, 0.0, 0.0); // fully desaturate
404 p->setPen(KColorUtils::darken(shadow, 0.1));
405 p->drawLine(QPointF(x+4, y-0.5), QPointF(x+w-4, y-0.5));
406 p->drawArc(QRectF(x-0.5, y-0.5, 11, 11),90*16, 90*16);
407 p->drawArc(QRectF(x+w-11+0.5, y-0.5, 11, 11), 0, 90*16);
408 p->setPen(KColorUtils::darken(shadow, 0.3));
409 p->drawLine(QPointF(x-0.5, y+4), QPointF(x-0.5, y+h-4));
410 p->drawLine(QPointF(x+w+0.5, y+4), QPointF(x+w+0.5, y+h-4));
411 p->setPen(KColorUtils::darken(shadow, 0.4));
412 p->drawArc(QRectF(0.5, y+h-11+0.5, 11, 11),180*16, 90*16);
413 p->drawArc(QRectF(x+w-11+0.5, y+h-11+0.5, 11, 11),270*16, 90*16);
414 p->setPen(KColorUtils::darken(shadow, 0.55));
415 p->drawLine(QPointF(x+4, y+h+0.5), QPointF(x+w-4, y+h+0.5));
418 p->setPen(QPen(light, 1.2));
419 p->drawLine(QPointF(x+4, y+0.6), QPointF(x+w-4, y+0.6));
420 QLinearGradient lg = QLinearGradient(0.0, 1.5, 0.0, 4.5);
421 lg.setColorAt(0, light);
422 light = calcLightColor(backgroundBottomColor(color));
423 lg.setColorAt(1, light);
424 p->setPen(QPen(lg, 1.2));
425 p->drawArc(QRectF(x+0.6, y+0.6, 9, 9),90*16, 90*16);
426 p->drawArc(QRectF(x+w-9-0.6, y+0.6, 9, 9), 0, 90*16);
427 p->drawLine(QPointF(x+0.6, y+4), QPointF(x+0.6, y+h-4));
428 p->drawLine(QPointF(x+w-0.6, y+4), QPointF(x+w-0.6, y+h-4));
430 lg = QLinearGradient(0.0, y+h-4.5, 0.0, y+h-0.5);
431 lg.setColorAt(0, light);
432 lg.setColorAt(1, dark);
433 p->setPen(QPen(lg, 1.2));
434 p->drawArc(QRectF(x+0.6, y+h-9-0.6, 9, 9),180*16, 90*16);
435 p->drawArc(QRectF(x+w-9-0.6, y+h-9-0.6, 9, 9), 270*16, 90*16);
436 p->drawLine(QPointF(x+4, y+h-0.6), QPointF(x+w-4, y+h-0.6));
437 return;