MuseScore/mstyle/stylehelper.cpp
2012-05-26 14:49:10 +02:00

1988 lines
74 KiB
C++

//=============================================================================
// MuseScore
// Linux Music Score Editor
// $Id:$
//
// Copyright (C) 2010 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "stylehelper.h"
#include "colorscheme.h"
#include "colorutils.h"
const qreal StyleHelper::_slabThickness = 0.45;
const qreal StyleHelper::_shadowGain = 1.5;
const qreal StyleHelper::_glowBias = 0.6;
//---------------------------------------------------------
// StyleHelper
//---------------------------------------------------------
StyleHelper::StyleHelper()
{
_contrast = .7;
_bgcontrast = qMin(1.0, 0.9 * _contrast/.7);
m_dockFrameCache.setMaxCost(1);
m_scrollHoleCache.setMaxCost(10);
m_backgroundCache.setMaxCost(64);
m_windecoButtonCache.setMaxCost(64);
m_windecoButtonGlowCache.setMaxCost(64);
}
//---------------------------------------------------------
// reloadConfig
//---------------------------------------------------------
void StyleHelper::reloadConfig()
{
// _config->reparseConfiguration();
// _contrast = KGlobalSettings::contrastF(_config);
_bgcontrast = qMin(1.0, 0.9*_contrast/0.7);
_viewFocusBrush = StatefulBrush(ColorScheme::View, ColorScheme::FocusColor);
_viewHoverBrush = StatefulBrush(ColorScheme::View, ColorScheme::HoverColor);
_viewNegativeTextBrush = StatefulBrush(ColorScheme::View, ColorScheme::NegativeText);
}
//---------------------------------------------------------
// invalidateCaches
//---------------------------------------------------------
void StyleHelper::invalidateCaches()
{
m_dialSlabCache.clear();
m_roundSlabCache.clear();
m_holeFocusedCache.clear();
m_midColorCache.clear();
m_progressBarCache.clear();
m_cornerCache.clear();
m_selectionCache.clear();
m_slabSunkenCache.clear();
m_slabInvertedCache.clear();
m_holeCache.clear();
m_holeFlatCache.clear();
m_slopeCache.clear();
m_grooveCache.clear();
m_slitCache.clear();
m_dockFrameCache.clear();
m_scrollHoleCache.clear();
m_slabCache.clear();
m_decoColorCache.clear();
m_lightColorCache.clear();
m_darkColorCache.clear();
m_shadowColorCache.clear();
m_backgroundTopColorCache.clear();
m_backgroundBottomColorCache.clear();
m_backgroundRadialColorCache.clear();
m_backgroundColorCache.clear();
m_backgroundCache.clear();
m_dotCache.clear();
m_windecoButtonCache.clear();
m_windecoButtonGlowCache.clear();
}
//---------------------------------------------------------
// setMaxCacheSize
//---------------------------------------------------------
void StyleHelper::setMaxCacheSize( int value )
{
m_windecoButtonCache.setMaxCost( value );
m_windecoButtonGlowCache.setMaxCost( value );
m_slabCache.setMaxCacheSize( value );
m_backgroundCache.setMaxCost( value );
m_dotCache.setMaxCost( value );
m_dialSlabCache.setMaxCacheSize( value );
m_roundSlabCache.setMaxCacheSize( value );
m_holeFocusedCache.setMaxCacheSize( value );
m_progressBarCache.setMaxCost( value );
m_cornerCache.setMaxCost( value );
m_selectionCache.setMaxCost( value );
m_slabSunkenCache.setMaxCost( value );
m_slabInvertedCache.setMaxCost( value );
m_holeCache.setMaxCost( value );
m_holeFlatCache.setMaxCost( value );
m_slopeCache.setMaxCost( value );
m_grooveCache.setMaxCost( value );
m_slitCache.setMaxCost( value );
m_dockFrameCache.setMaxCost( value );
m_scrollHoleCache.setMaxCost( value );
}
//---------------------------------------------------------
// checkAutoFillBackground
//---------------------------------------------------------
const QWidget* StyleHelper::checkAutoFillBackground(const QWidget* w) const
{
if (!w)
return 0;
if (w->autoFillBackground())
return w;
if (w->isWindow())
return 0;
for (const QWidget* parent = w->parentWidget(); parent; parent = parent->parentWidget()) {
if (parent->autoFillBackground())
return parent;
if (parent == w->window())
break;
}
return 0;
}
//---------------------------------------------------------
// backgroundColor
//---------------------------------------------------------
const QColor& StyleHelper::backgroundColor(const QColor &color, qreal ratio) const
{
const quint64 key((quint64(color.rgba()) << 32) | int(ratio*512));
QColor* out = m_backgroundColorCache.object(key);
if (!out) {
if (ratio < 0.5) {
const qreal a(2.0 * ratio);
out = new QColor(ColorUtils::mix(backgroundTopColor(color), color, a));
}
else {
const qreal a( 2.0*ratio-1 );
out = new QColor(ColorUtils::mix(color, backgroundBottomColor(color), a));
}
m_backgroundColorCache.insert(key, out);
}
return *out;
}
//---------------------------------------------------------
// backgroundTopColor
//---------------------------------------------------------
const QColor& StyleHelper::backgroundTopColor(const QColor &color) const
{
const quint64 key(color.rgba());
QColor* out(m_backgroundTopColorCache.object(key));
if (!out) {
if (lowThreshold(color) )
out = new QColor(ColorScheme::shade(color, ColorScheme::MidlightShade, 0.0) );
else {
const qreal my(ColorUtils::luma(ColorScheme::shade(color, ColorScheme::LightShade, 0.0) ) );
const qreal by(ColorUtils::luma(color) );
out = new QColor(ColorUtils::shade(color, (my - by) * _bgcontrast) );
}
m_backgroundTopColorCache.insert( key, out );
}
return *out;
}
//---------------------------------------------------------
// backgroundBottomColor
//---------------------------------------------------------
const QColor& StyleHelper::backgroundBottomColor(const QColor &color) const
{
const quint64 key( color.rgba() );
QColor* out(m_backgroundBottomColorCache.object(key));
if (!out) {
const QColor midColor(ColorScheme::shade(color, ColorScheme::MidShade, 0.0));
if (lowThreshold(color))
out = new QColor(midColor);
else {
const qreal by(ColorUtils::luma(color) );
const qreal my(ColorUtils::luma(midColor) );
out = new QColor(ColorUtils::shade(color, (my - by) * _bgcontrast) );
}
m_backgroundBottomColorCache.insert( key, out );
}
return *out;
}
//---------------------------------------------------------
// lowThreshold
//---------------------------------------------------------
bool StyleHelper::lowThreshold(const QColor &color) const
{
const quint32 key( color.rgba() );
ColorMap::iterator iter( m_lowThreshold.find( key ) );
if (iter != m_lowThreshold.end())
return iter.value();
else {
const QColor darker(ColorScheme::shade(color, ColorScheme::MidShade, 0.5 ) );
const bool result(ColorUtils::luma(darker) > ColorUtils::luma(color) );
m_lowThreshold.insert(key, result);
return result;
}
}
//---------------------------------------------------------
// highThreshold
//---------------------------------------------------------
bool StyleHelper::highThreshold(const QColor &color) const
{
const quint32 key( color.rgba() );
ColorMap::iterator iter( m_highThreshold.find( key ) );
if (iter != m_highThreshold.end() )
return iter.value();
else {
const QColor lighter(ColorScheme::shade(color, ColorScheme::LightShade, 0.5 ) );
const bool result(ColorUtils::luma(lighter) < ColorUtils::luma(color) );
m_highThreshold.insert(key, result);
return result;
}
}
//---------------------------------------------------------
// hole
//---------------------------------------------------------
TileSet* StyleHelper::hole(const QColor &color, qreal shade, int size, bool outline) const
{
const quint64 key( (quint64(color.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size << 1 | outline );
TileSet *tileSet = m_holeCache.object(key);
if (!tileSet) {
const int rsize( (int)ceil(qreal(size) * 5.0/7.0 ) );
QImage image(rsize*2, rsize*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(2,2,10,10);
// hole mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush(Qt::black);
p.drawEllipse(3,3,8,8);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
if( outline ) {
QLinearGradient blend( 0, 3, 0, 11 );
blend.setColorAt(0, Qt::transparent );
blend.setColorAt(1, calcDarkColor( color ) );
p.setBrush( Qt::NoBrush );
p.setPen( QPen( blend, 1 ) );
p.drawEllipse( 3, 3.5, 8, 7 );
p.setPen( Qt::NoPen );
}
// shadow
drawInverseShadow(p, calcShadowColor( color ), 3, 8, 0.0);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), rsize, rsize, rsize, rsize, rsize-1, rsize, 2, 1);
m_holeCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// holeFlat
//---------------------------------------------------------
TileSet* StyleHelper::holeFlat(const QColor& color, qreal shade, int size) const
{
const quint64 key((quint64(color.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size );
TileSet *tileSet = m_holeFlatCache.object(key);
if (!tileSet) {
const int rsize((int)ceil(qreal(size) * 5.0/7.0) );
QImage image(rsize*2, rsize*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(2,2,10,10);
// hole
drawHole(p, color, shade, 7);
// hole inside
p.setBrush(color);
p.drawEllipse(QRectF(3.4,3.4,7.2,7.2));
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), rsize, rsize, rsize, rsize, rsize-1, rsize, 2, 1);
m_holeFlatCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// drawHole
//---------------------------------------------------------
void StyleHelper::drawHole(QPainter &p, const QColor &color, qreal shade, int r) const
{
const int r2( 2*r );
const QColor base(ColorUtils::shade(color, shade) );
const QColor light(ColorUtils::shade(calcLightColor(color), shade) );
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade) );
const QColor mid(ColorUtils::shade(calcMidColor(color), shade) );
// bevel
const qreal y(ColorUtils::luma(base) );
const qreal yl(ColorUtils::luma(light) );
const qreal yd(ColorUtils::luma(dark) );
QLinearGradient bevelGradient1(0, 2, 0, r2-2);
bevelGradient1.setColorAt(0.2, dark);
bevelGradient1.setColorAt(0.5, mid);
bevelGradient1.setColorAt(1.0, light);
if (y < yl && y > yd) {
// no middle when color is very light/dark
bevelGradient1.setColorAt(0.6, base);
}
p.setBrush(bevelGradient1);
p.drawEllipse(3,3,r2-6,r2-6);
// mask
QRadialGradient maskGradient(r,r,r-2);
maskGradient.setColorAt(0.80, Qt::black );
maskGradient.setColorAt(0.90, alphaColor( Qt::black,0.55) );
maskGradient.setColorAt(1.00, Qt::transparent );
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.setBrush(maskGradient);
p.drawRect(0,0,r2,r2);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
}
//---------------------------------------------------------
// fillHole
//---------------------------------------------------------
void StyleHelper::fillHole(QPainter &p, const QRect &rect, int size) const
{
const qreal s( (3.0*size)/7.0 );
p.drawRoundedRect(rect.adjusted(s,s,-s,-s), 4, 4);
}
//---------------------------------------------------------
// renderHole
//---------------------------------------------------------
void StyleHelper::renderHole(QPainter *p, const QColor &base, const QRect &r,
bool focus, bool hover, qreal opacity, AnimationMode animationMode, TileSet::Tiles tiles, bool outline) const
{
if( !r.isValid() )
return;
if( opacity >= 0 && ( animationMode & AnimationFocus ) ) {
// calculate proper glow color based on current settings and opacity
const QColor glow( hover ?
ColorUtils::mix( viewHoverBrush().brush(QPalette::Active).color(), viewFocusBrush().brush(QPalette::Active).color(), opacity ):
alphaColor( viewFocusBrush().brush(QPalette::Active).color(), opacity ) );
holeFocused(base, glow, 0.0, 7, outline)->render(r, p, tiles);
}
else if (focus) {
holeFocused(base, viewFocusBrush().brush(QPalette::Active).color(), 0.0)->render(r, p, tiles);
}
else if( opacity >= 0 && ( animationMode & AnimationHover ) ) {
// calculate proper glow color based on current settings and opacity
const QColor glow( alphaColor( viewHoverBrush().brush(QPalette::Active).color(), opacity ) );
holeFocused(base, glow, 0.0, 7, outline)->render(r, p, tiles);
}
else if (hover) {
holeFocused(base, viewHoverBrush().brush(QPalette::Active).color(), 0.0)->render(r, p, tiles);
}
else {
hole(base, 0.0, 7, outline)->render(r, p, tiles);
}
}
//---------------------------------------------------------
// holeFocused
//---------------------------------------------------------
TileSet* StyleHelper::holeFocused(const QColor &color, const QColor &glowColor, qreal shade, int size, bool outline) const
{
// FIXME must move to s/slabcache/cache/ b/c key is wrong
Cache<TileSet>::Value* cache(m_holeFocusedCache.get(glowColor) );
const quint64 key( (quint64(color.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size << 1 | outline );
TileSet *tileSet = cache->object(key);
if (!tileSet) {
const int rsize( (int)ceil(qreal(size) * 5.0/7.0) );
QImage image(rsize*2, rsize*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
TileSet *holeTileSet = hole(color, shade, size, outline);
holeTileSet->render(QRect(0,0,10,10), &p);
p.setWindow(2,2,10,10);
drawInverseGlow(p, glowColor, 3, 8, size);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), rsize, rsize, rsize, rsize, rsize-1, rsize, 2, 1);
cache->insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// scrollHole
//---------------------------------------------------------
TileSet* StyleHelper::scrollHole(const QColor &color, Qt::Orientation orientation, bool smallShadow) const
{
const quint64 key( quint64(color.rgba()) << 32 | (orientation == Qt::Horizontal ? 2 : 0) | (smallShadow ? 1 : 0) );
TileSet *tileSet = m_scrollHoleCache.object(key);
if (!tileSet) {
QImage image(15, 15, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
const QColor dark( calcDarkColor(color) );
const QColor light( calcLightColor(color) );
const QColor shadow( calcShadowColor(color) );
// use space for white border
const QRect r( QRect(0,0,15,15) );
const QRect rect( r.adjusted(1, 0, -1, -1) );
int shadowWidth(0);
if( smallShadow )
shadowWidth = (orientation == Qt::Horizontal) ? 2 : 1;
else
shadowWidth = (orientation == Qt::Horizontal) ? 3 : 2;
p.setRenderHints(QPainter::Antialiasing);
p.setBrush(dark);
p.setPen(Qt::NoPen);
// base
p.drawRoundedRect(rect, 4.5, 4.5);
// slight shadow across the whole hole
QLinearGradient shadowGradient(rect.topLeft(),
orientation == Qt::Horizontal ? rect.bottomLeft():rect.topRight());
shadowGradient.setColorAt(0.0, alphaColor(shadow, 0.1));
shadowGradient.setColorAt(0.6, Qt::transparent);
p.setBrush(shadowGradient);
p.drawRoundedRect(rect, 4.5, 4.5);
// strong shadow
// left
QLinearGradient l1 = QLinearGradient(rect.topLeft(), rect.topLeft()+QPoint(shadowWidth,0));
l1.setColorAt(0.0, alphaColor(shadow, orientation == Qt::Horizontal ? 0.3 : 0.2));
l1.setColorAt(0.5, alphaColor(shadow, orientation == Qt::Horizontal ? 0.1 : 0.1));
l1.setColorAt(1.0, Qt::transparent);
p.setBrush(l1);
p.drawRoundedRect(QRect(rect.topLeft(), rect.bottomLeft()+QPoint(shadowWidth,0)), 4.5, 4.5);
// right
l1 = QLinearGradient(rect.topRight(), rect.topRight()-QPoint(shadowWidth,0));
l1.setColorAt(0.0, alphaColor(shadow, orientation == Qt::Horizontal ? 0.3 : 0.2));
l1.setColorAt(0.5, alphaColor(shadow, orientation == Qt::Horizontal ? 0.1 : 0.1));
l1.setColorAt(1.0, Qt::transparent);
p.setBrush(l1);
p.drawRoundedRect(QRect(rect.topRight()-QPoint(shadowWidth,0), rect.bottomRight()), 4.5, 4.5);
//top
l1 = QLinearGradient(rect.topLeft(), rect.topLeft()+QPoint(0,3));
l1.setColorAt(0.0, alphaColor(shadow, 0.3));
l1.setColorAt(1.0, Qt::transparent);
p.setBrush(l1);
p.drawRoundedRect(QRect(rect.topLeft(),rect.topRight()+QPoint(0,3)), 4.5, 4.5);
// light border
QLinearGradient borderGradient(r.topLeft()+QPoint(0,r.height()/2-1), r.bottomLeft());
borderGradient.setColorAt(0.0, Qt::transparent);
borderGradient.setColorAt(1.0, alphaColor(light, 0.8));
p.setPen( QPen(borderGradient, 1.0) );
p.setBrush(Qt::NoBrush);
p.drawRoundedRect(r.adjusted(0.5,0,-0.5,0), 5.0, 5.0);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), 7, 7, 1, 1);
m_scrollHoleCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// calcLightColor
//---------------------------------------------------------
const QColor& StyleHelper::calcLightColor(const QColor &color) const
{
const quint64 key( color.rgba() );
QColor* out( m_lightColorCache.object( key ) );
if (!out) {
out = new QColor( highThreshold(color) ? color: ColorScheme::shade(color, ColorScheme::LightShade, _contrast) );
m_lightColorCache.insert(key, out );
}
return *out;
}
//---------------------------------------------------------
// calcDarkColor
//---------------------------------------------------------
const QColor& StyleHelper::calcDarkColor(const QColor &color) const
{
const quint64 key( color.rgba() );
QColor* out( m_darkColorCache.object( key ) );
if ( !out ) {
out = new QColor( (lowThreshold(color)) ?
ColorUtils::mix(calcLightColor(color), color, 0.3 + 0.7 * _contrast):
ColorScheme::shade(color, ColorScheme::MidShade, _contrast) );
m_darkColorCache.insert(key, out );
}
return *out;
}
//---------------------------------------------------------
// alphaColor
//---------------------------------------------------------
QColor StyleHelper::alphaColor(QColor color, qreal alpha)
{
if (alpha >= 0 && alpha < 1.0) {
color.setAlphaF(alpha*color.alphaF());
}
return color;
}
//---------------------------------------------------------
// drawInverseShadow
//---------------------------------------------------------
void StyleHelper::drawInverseShadow(
QPainter &p, const QColor &color, int pad, int size, qreal fuzz ) const
{
const qreal m( qreal(size)*0.5 );
const QColor shadow( calcShadowColor( color ) );
const qreal offset( 0.8 );
const qreal k0( (m-2) / qreal(m+2.0) );
QRadialGradient shadowGradient(pad+m, pad+m+offset, m+2);
for (int i = 0; i < 8; i++) {
// sinusoidal gradient
qreal k1 = (qreal(8 - i) + k0 * qreal(i)) * 0.125;
qreal a = (cos(3.14159 * i * 0.125) + 1.0) * 0.25;
shadowGradient.setColorAt(k1, alphaColor(shadow, a * _shadowGain));
}
shadowGradient.setColorAt(k0, alphaColor(color, 0.0));
p.setBrush(shadowGradient);
p.drawEllipse(QRectF(pad-fuzz, pad-fuzz, size+fuzz*2.0, size+fuzz*2.0));
}
//---------------------------------------------------------
// drawInverseGlow
//---------------------------------------------------------
void StyleHelper::drawInverseGlow(
QPainter &p, const QColor &color,
int pad, int size, int rsize) const
{
const QRectF r(pad, pad, size, size);
const qreal m( qreal(size)*0.5 );
const qreal width( 3.5 );
const qreal bias( _glowBias*7.0/rsize );
const qreal k0( (m-width)/(m-bias) );
QRadialGradient glowGradient(pad+m, pad+m, m-bias);
for (int i = 0; i < 8; i++)
{
// inverse parabolic gradient
qreal k1 = (k0 * qreal(i) + qreal(8 - i)) * 0.125;
qreal a = 1.0 - sqrt(i * 0.125);
glowGradient.setColorAt(k1, alphaColor(color, a));
}
glowGradient.setColorAt(k0, alphaColor(color, 0.0));
p.setBrush(glowGradient);
p.drawEllipse(r);
}
//---------------------------------------------------------
// calcShadowColor
//---------------------------------------------------------
const QColor& StyleHelper::calcShadowColor(const QColor &color) const
{
const quint64 key( color.rgba() );
QColor* out( m_shadowColorCache.object( key ) );
if( !out ) {
out = new QColor( (lowThreshold(color)) ?
ColorUtils::mix( Qt::black, color, color.alphaF() ) :
ColorScheme::shade(ColorUtils::mix( Qt::black, color, color.alphaF() ),
ColorScheme::ShadowShade, _contrast) );
m_shadowColorCache.insert(key, out );
}
return *out;
}
//---------------------------------------------------------
// renderMenuBackground
//---------------------------------------------------------
void StyleHelper::renderMenuBackground(QPainter* p, const QRect& clipRect, const QWidget* widget, const QColor& color) const
{
// get coordinates relative to the client area
// this is stupid. One could use mapTo if this was taking const QWidget* and not
// QWidget* as argument.
const QWidget* w( widget );
int x(0);
int y(0);
while( !w->isWindow() && w != w->parentWidget() ) {
x += w->geometry().x();
y += w->geometry().y();
w = w->parentWidget();
}
if (clipRect.isValid()) {
p->save();
p->setClipRegion(clipRect,Qt::IntersectClip);
}
// calculate upper part height
// special tricks are needed
// to handle both window contents and window decoration
QRect r = w->rect();
const int height( w->frameGeometry().height() );
const int splitY( qMin(200, (3*height)/4) );
const QRect upperRect( QRect(0, 0, r.width(), splitY) );
const QPixmap tile(verticalGradient(color, splitY));
p->drawTiledPixmap(upperRect, tile);
const QRect lowerRect( 0,splitY, r.width(), r.height() - splitY );
p->fillRect(lowerRect, backgroundBottomColor(color));
if (clipRect.isValid())
p->restore();
}
//---------------------------------------------------------
// verticalGradient
//---------------------------------------------------------
QPixmap StyleHelper::verticalGradient(const QColor &color, int height, int offset) const
{
const quint64 key((quint64(color.rgba()) << 32) | height | 0x8000);
QPixmap* pixmap(m_backgroundCache.object(key));
if (!pixmap) {
QImage image(1, height, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QLinearGradient gradient(0, offset, 0, height+offset);
gradient.setColorAt(0.0, backgroundTopColor(color));
gradient.setColorAt(0.5, color);
gradient.setColorAt(1.0, backgroundBottomColor(color));
QPainter p(&image);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(image.rect(), gradient);
p.end();
pixmap = new QPixmap(QPixmap::fromImage(image));
m_backgroundCache.insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// radialGradient
//---------------------------------------------------------
QPixmap StyleHelper::radialGradient(const QColor &color, int width, int height) const
{
const quint64 key((quint64(color.rgba()) << 32) | width | 0xb000);
QPixmap *pixmap(m_backgroundCache.object(key));
if (!pixmap) {
QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QColor radialColor = backgroundRadialColor(color);
printf("radialGradient %d %d %d %d %d\n", width, height,
radialColor.red(), radialColor.green(), radialColor.blue());
radialColor.setAlpha(255);
QRadialGradient gradient(64, height-64, 64);
gradient.setColorAt(0, radialColor);
radialColor.setAlpha(101);
gradient.setColorAt(0.5, radialColor);
radialColor.setAlpha(37);
gradient.setColorAt(0.75, radialColor);
radialColor.setAlpha(0);
gradient.setColorAt(1, radialColor);
QPainter p(&image);
p.scale(width/128.0, 1);
p.fillRect(QRect(0,0,128,height), gradient);
p.end();
pixmap = new QPixmap(QPixmap::fromImage(image));
m_backgroundCache.insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// backgroundRadialColor
//---------------------------------------------------------
const QColor& StyleHelper::backgroundRadialColor(const QColor &color) const
{
const quint64 key( color.rgba() );
QColor* out( m_backgroundRadialColorCache.object( key ) );
if (!out) {
if( lowThreshold(color) )
out = new QColor(ColorScheme::shade(color, ColorScheme::LightShade, 0.0) );
else if( highThreshold(color))
out = new QColor(color);
else out = new QColor(ColorScheme::shade(color, ColorScheme::LightShade, _bgcontrast) );
m_backgroundRadialColorCache.insert( key, out );
}
return *out;
}
//---------------------------------------------------------
// renderWindowBackground
//---------------------------------------------------------
void StyleHelper::renderWindowBackground(QPainter* p, const QRect& clipRect, const QWidget* widget,
const QWidget* window, const QColor& color, int y_shift, int gradientHeight) const
{
// get coordinates relative to the client area
// this is stupid. One could use mapTo if this was taking const QWidget* and not
// QWidget* as argument.
const QWidget* w( widget );
int x(0);
int y(-y_shift);
while (w != window && !w->isWindow() && w != w->parentWidget()) {
x += w->geometry().x();
y += w->geometry().y();
w = w->parentWidget();
}
if (clipRect.isValid()) {
p->save();
p->setClipRegion(clipRect,Qt::IntersectClip);
}
// calculate upper part height
// special tricks are needed
// to handle both window contents and window decoration
const QRect r = window->rect();
int height = window->frameGeometry().height();
int width = window->frameGeometry().width();
if (y_shift > 0) {
height -= 2*y_shift;
width -= 2*y_shift;
}
const int splitY(qMin(300, (3*height)/4) );
// draw upper linear gradient
const QRect upperRect(-x, -y, r.width(), splitY);
QPixmap tile( verticalGradient(color, splitY, gradientHeight-64) );
p->drawTiledPixmap(upperRect, tile);
// draw lower flat part
const QRect lowerRect(-x, splitY-y, r.width(), r.height() - splitY-y_shift);
p->fillRect(lowerRect, backgroundBottomColor(color));
// draw upper radial gradient
// WS: dont know how this should look like, but its
// obviously wrong on MAC:
#if 0 //DEBUG
const int radialW (qMin(600, width));
const QRect radialRect( (r.width() - radialW) / 2-x, -y, radialW, gradientHeight);
if (clipRect.intersects(radialRect)) {
tile = radialGradient(color, radialW, gradientHeight);
p->drawPixmap(radialRect, tile);
}
#endif
if (clipRect.isValid())
p->restore();
}
//---------------------------------------------------------
// renderDot
//---------------------------------------------------------
void StyleHelper::renderDot(QPainter *p, const QPoint &point, const QColor &baseColor) const
{
const quint64 key(baseColor.rgba());
QPixmap* pixmap(m_dotCache.object(key));
if (!pixmap) {
pixmap = new QPixmap( 4, 4 );
pixmap->fill( Qt::transparent );
const qreal diameter( 1.8 );
QPainter painter( pixmap );
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
const QPoint center( pixmap->rect().center() );
// light ellipse
painter.setBrush(calcLightColor(baseColor));
painter.drawEllipse(QRectF(center.x()-diameter/2+1.0, center.y()-diameter/2+1.0, diameter, diameter));
// dark ellipse
painter.setBrush(calcDarkColor(baseColor).darker(130));
painter.drawEllipse(QRectF(center.x()-diameter/2+0.5, center.y()-diameter/2+0.5, diameter, diameter));
painter.end();
// store in cache
m_dotCache.insert( key, pixmap );
}
p->save();
p->translate( point - QPoint(1,1) );
p->setRenderHint(QPainter::Antialiasing);
p->drawPixmap( QPoint(0,0), *pixmap );
p->restore();
}
//---------------------------------------------------------
// drawSeparator
//---------------------------------------------------------
void StyleHelper::drawSeparator(QPainter *p, const QRect &rect, const QColor &color, Qt::Orientation orientation) const
{
QColor light(calcLightColor(color));
QColor dark(calcDarkColor(color));
p->save();
p->setRenderHint(QPainter::Antialiasing,false);
QPoint start,end,offset;
if (orientation == Qt::Horizontal) {
start = QPoint(rect.x(),rect.y()+rect.height()/2-1);
end = QPoint(rect.right(),rect.y()+rect.height()/2-1);
offset = QPoint(0,1);
}
else {
start = QPoint(rect.x()+rect.width()/2-1,rect.y());
end = QPoint(rect.x()+rect.width()/2-1,rect.bottom());
offset = QPoint(1,0);
light.setAlpha(150);
}
QLinearGradient lg(start,end);
lg.setColorAt(0.3, dark);
lg.setColorAt(0.7, dark);
dark.setAlpha(0);
lg.setColorAt(0.0, dark);
lg.setColorAt(1.0, dark);
p->setPen(QPen(lg,1));
if (orientation == Qt::Horizontal)
p->drawLine(start,end);
else
p->drawLine(start+offset,end+offset);
lg = QLinearGradient(start,end);
lg.setColorAt(0.3, light);
lg.setColorAt(0.7, light);
light.setAlpha(0);
lg.setColorAt(0.0, light);
lg.setColorAt(1.0, light);
p->setPen(QPen(lg,1));
if (orientation == Qt::Horizontal)
p->drawLine(start+offset,end+offset);
else {
p->drawLine(start,end);
p->drawLine(start+offset*2,end+offset*2);
}
p->restore();
}
//---------------------------------------------------------
// decoColor
//---------------------------------------------------------
const QColor& StyleHelper::decoColor(const QColor& background, const QColor &color) const
{
const quint64 key( (quint64( background.rgba() ) << 32) | color.rgba() );
QColor* out( m_decoColorCache.object( key ) );
if (!out) {
out = new QColor(ColorUtils::mix( background, color, 0.4 + 0.8*_contrast ) );
m_decoColorCache.insert( key, out );
}
return *out;
}
//---------------------------------------------------------
// roundedMask
//---------------------------------------------------------
QRegion StyleHelper::roundedMask( const QRect& r, int left, int right, int top, int bottom ) const
{
// get rect geometry
int x, y, w, h;
r.getRect(&x, &y, &w, &h);
QRegion mask(x + 4*left, y + 0*top, w-4*(left+right), h-0*(top+bottom));
mask += QRegion(x + 0*left, y + 4*top, w-0*(left+right), h-4*(top+bottom));
mask += QRegion(x + 2*left, y + 1*top, w-2*(left+right), h-1*(top+bottom));
mask += QRegion(x + 1*left, y + 2*top, w-1*(left+right), h-2*(top+bottom));
return mask;
}
//---------------------------------------------------------
// fillSlab
//---------------------------------------------------------
void StyleHelper::fillSlab(QPainter &p, const QRect &rect, int size) const
{
const qreal s( qreal(size) * (3.6 + (0.5 * _slabThickness)) / 7.0 );
const QRectF r( QRectF(rect).adjusted(s, s, -s, -s) );
if( !r.isValid() )
return;
p.drawRoundedRect( r, s, s );
}
//---------------------------------------------------------
// progressBarIndicator
//---------------------------------------------------------
QPixmap StyleHelper::progressBarIndicator(const QPalette& pal, const QRect& rect) const
{
const QColor highlight( pal.color( QPalette::Highlight ) );
const quint64 key( (quint64(highlight.rgba()) << 32) | (rect.width() << 16 ) | (rect.height() ) );
QPixmap *pixmap = m_progressBarCache.object(key);
if (!pixmap) {
QRect local( rect );
local.adjust( -1, -2, 1, 1 );
// set topLeft corner to 0.0
local.translate( -local.topLeft() );
QImage image(local.size(), QImage::Format_ARGB32_Premultiplied);
image.fill( Qt::transparent );
QPainter p(&image);
p.setRenderHints( QPainter::Antialiasing );
p.setBrush(Qt::NoBrush);
local.adjust( 1, 1, -1, -1 );
const QColor lhighlight( calcLightColor(highlight) );
const QColor color( pal.color(QPalette::Active, QPalette::Window) );
const QColor light( calcLightColor(color) );
const QColor dark( calcDarkColor(color) );
const QColor shadow( calcShadowColor(color) );
// shadow
p.setPen(QPen(alphaColor(shadow, 0.6),0.6));
p.drawRoundedRect(QRectF(local).adjusted( 0.5, -0.5, 0.5, 1.5), 2, 2 );
// fill
p.setPen(Qt::NoPen);
p.setBrush(ColorUtils::mix(highlight, dark, 0.2));
p.drawRect(local.adjusted(1, 0, -1, 0 ) );
// fake radial gradient
local.adjust( 0, 0, -1, 0 );
QImage pm(local.size(), QImage::Format_ARGB32_Premultiplied);
pm.fill(Qt::transparent);
{
QRectF pmRect = pm.rect();
QLinearGradient mask(pmRect.topLeft(), pmRect.topRight());
mask.setColorAt(0.0, Qt::transparent);
mask.setColorAt(0.4, Qt::black);
mask.setColorAt(0.6, Qt::black);
mask.setColorAt(1.0, Qt::transparent);
QLinearGradient radial(pmRect.topLeft(), pmRect.bottomLeft());
radial.setColorAt(0.0, ColorUtils::mix(lhighlight, light, 0.3));
radial.setColorAt(0.5, Qt::transparent);
radial.setColorAt(0.6, Qt::transparent);
radial.setColorAt(1.0, ColorUtils::mix(lhighlight, light, 0.3));
QPainter pp(&pm);
pp.fillRect(pm.rect(), mask);
pp.setCompositionMode(QPainter::CompositionMode_SourceIn);
pp.fillRect(pm.rect(), radial);
pp.end();
}
p.drawPixmap( QPoint(1,1), QPixmap::fromImage(pm));
// bevel
p.setRenderHint(QPainter::Antialiasing, false);
QLinearGradient bevel(local.topLeft(), local.bottomLeft());
bevel.setColorAt(0, lhighlight);
bevel.setColorAt(0.5, highlight);
bevel.setColorAt(1, calcDarkColor(highlight));
p.setBrush(Qt::NoBrush);
p.setPen(QPen(bevel, 1));
p.drawRoundedRect(local,2,2);
// bright top edge
QLinearGradient lightHl(local.topLeft(),local.topRight());
lightHl.setColorAt(0, Qt::transparent);
lightHl.setColorAt(0.5, ColorUtils::mix(highlight, light, 0.8));
lightHl.setColorAt(1, Qt::transparent);
p.setPen(QPen(lightHl, 1));
p.drawLine(local.topLeft(), local.topRight());
p.end();
pixmap = new QPixmap(QPixmap::fromImage(image));
m_progressBarCache.insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// dialSlab
//---------------------------------------------------------
QPixmap StyleHelper::dialSlab(const QColor &color, qreal shade, int size) const
{
Cache<QPixmap>::Value *cache = m_dialSlabCache.get(color);
const quint64 key( (quint64(256.0 * shade) << 24) | size );
QPixmap *pixmap = cache->object(key);
if (!pixmap) {
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.fill( Qt::transparent );
const QRectF rect(image.rect() );
QPainter p(&image);
p.setPen( Qt::NoPen );
p.setRenderHints(QPainter::Antialiasing);
// colors
const QColor base(ColorUtils::shade(color, shade) );
const QColor light(ColorUtils::shade(calcLightColor(color), shade) );
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade) );
const QColor mid(ColorUtils::shade(calcMidColor(color), shade) );
const QColor shadow( calcShadowColor(color) );
// shadow
drawShadow( p, shadow, rect.width() );
const qreal baseOffset = 3.5;
{
//plain background
QLinearGradient lg( 0, baseOffset-0.5*rect.height(), 0, baseOffset+rect.height() );
lg.setColorAt( 0, light );
lg.setColorAt( 0.8, base );
p.setBrush( lg );
const qreal offset( baseOffset );
p.drawEllipse( rect.adjusted( offset, offset, -offset, -offset ) );
}
{
// outline circle
const qreal penWidth( 0.7 );
QLinearGradient lg( 0, baseOffset, 0, baseOffset + 2*rect.height() );
lg.setColorAt( 0, light );
lg.setColorAt( 1, mid );
p.setBrush( Qt::NoBrush );
p.setPen( QPen( lg, penWidth ) );
const qreal offset( baseOffset+0.5*penWidth );
p.drawEllipse( rect.adjusted( offset, offset, -offset, -offset ) );
}
pixmap = new QPixmap(QPixmap::fromImage(image));
cache->insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// dialSlabFocused
//---------------------------------------------------------
QPixmap StyleHelper::dialSlabFocused(const QColor &color, const QColor& glowColor, qreal shade, int size) const
{
return QPixmap();
Cache<QPixmap>::Value* cache = m_dialSlabCache.get(color);
const quint64 key((quint64(glowColor.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size);
QPixmap *pixmap = cache->object(key);
if (!pixmap) {
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QRectF rect(image.rect());
QPainter p(&image);
p.setPen( Qt::NoPen );
p.setRenderHints(QPainter::Antialiasing);
// colors
const QColor base(ColorUtils::shade(color, shade));
const QColor light(ColorUtils::shade(calcLightColor(color), shade));
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade));
const QColor mid(ColorUtils::shade(calcMidColor(color), shade));
const QColor shadow(calcShadowColor(color));
// shadow
drawShadow(p, shadow, rect.width());
drawOuterGlow(p, glowColor, rect.width());
const qreal baseOffset(3.5);
{
//plain background
QLinearGradient lg(0, baseOffset-0.5*rect.height(), 0, baseOffset+rect.height());
lg.setColorAt(0, light);
lg.setColorAt(0.8, base);
p.setBrush(lg);
const qreal offset(baseOffset);
p.drawEllipse(rect.adjusted( offset, offset, -offset, -offset));
}
{
// outline circle
const qreal penWidth(0.7);
QLinearGradient lg(0, baseOffset, 0, baseOffset + 2*rect.height());
lg.setColorAt(0, light);
lg.setColorAt(1, mid);
p.setBrush(Qt::NoBrush);
p.setPen(QPen(lg, penWidth));
const qreal offset(baseOffset+0.5*penWidth );
p.drawEllipse(rect.adjusted( offset, offset, -offset, -offset));
}
pixmap = new QPixmap(QPixmap::fromImage(image));
cache->insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// roundSlab
//---------------------------------------------------------
QPixmap StyleHelper::roundSlab(const QColor &color, qreal shade, int size) const
{
Cache<QPixmap>::Value* cache(m_roundSlabCache.get(color));
const quint64 key( (quint64(256.0 * shade) << 24) | size);
QPixmap *pixmap = cache->object(key);
if (!pixmap) {
QImage image(size*3, size*3, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(0,0,21,21);
// shadow
drawShadow(p, calcShadowColor(color), 21);
drawRoundSlab(p, color, shade);
p.end();
pixmap = new QPixmap(QPixmap::fromImage(image));
cache->insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// roundSlabFocused
//---------------------------------------------------------
QPixmap StyleHelper::roundSlabFocused(const QColor &color, const QColor &glowColor, qreal shade, int size) const
{
Cache<QPixmap>::Value* cache(m_roundSlabCache.get(color));
const quint64 key((quint64(glowColor.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size);
QPixmap *pixmap = cache->object(key);
if (!pixmap) {
QImage image(size*3, size*3, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(0,0,21,21);
drawShadow(p, calcShadowColor(color), 21);
drawOuterGlow(p, glowColor, 21);
drawRoundSlab(p, color, shade);
p.end();
pixmap = new QPixmap(QPixmap::fromImage(image));
cache->insert(key, pixmap);
}
return *pixmap;
}
//---------------------------------------------------------
// slabFocused
//---------------------------------------------------------
TileSet *StyleHelper::slabFocused(const QColor &color, const QColor &glowColor, qreal shade, int size) const
{
Cache<TileSet>::Value* cache( m_slabCache.get(color) );
const quint64 key( (quint64(glowColor.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size );
TileSet *tileSet = cache->object(key);
const qreal hScale( 1 );
const int hSize( size*hScale );
const int vSize( size );
if (!tileSet) {
QImage image(hSize*2, vSize*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
const int fixedSize = 14;
p.setWindow(0,0,fixedSize*hScale, fixedSize);
// draw all components
if (color.isValid())
drawShadow(p, calcShadowColor(color), 14);
if (glowColor.isValid())
drawOuterGlow(p, glowColor, 14);
if (color.isValid())
drawSlab(p, color, shade);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), hSize, vSize, hSize, vSize, hSize-1, vSize, 2, 1);
cache->insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// slabSunken
//---------------------------------------------------------
TileSet *StyleHelper::slabSunken(const QColor &color, qreal shade, int size) const
{
const quint64 key( (quint64(color.rgba()) << 32) );
TileSet *tileSet = m_slabSunkenCache.object(key);
if (!tileSet) {
QImage image(size*2, size*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(0,0,14,14);
// slab
drawSlab(p, color, shade);
// shadow
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
drawInverseShadow(p, calcShadowColor(color), 3, 8, 0.0);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), size, size, size, size, size-1, size, 2, 1);
m_slabSunkenCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// slabInverted
//---------------------------------------------------------
TileSet *StyleHelper::slabInverted(const QColor &color, qreal shade, int size) const
{
const quint64 key( (quint64(color.rgba()) << 32) );
TileSet *tileSet = m_slabInvertedCache.object(key);
if (!tileSet) {
QImage image(size*2, size*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(0,0,14,14);
const QColor base(ColorUtils::shade(color, shade) );
const QColor light(ColorUtils::shade(calcLightColor(color), shade) );
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade) );
// bevel, part 2
QLinearGradient bevelGradient2(0, 8, 0, -8);
bevelGradient2.setColorAt(0.0, light);
bevelGradient2.setColorAt(0.9, base);
p.setBrush(bevelGradient2);
p.drawEllipse(QRectF(2.6,2.6,8.8,8.8));
// bevel, part 1
qreal y = ColorUtils::luma(base);
qreal yl = ColorUtils::luma(light);
qreal yd = ColorUtils::luma(dark);
QLinearGradient bevelGradient1(0, 7, 0, 4);
bevelGradient1.setColorAt(0.0, light);
bevelGradient1.setColorAt(0.9, dark);
if (y < yl && y > yd) {
// no middle when color is very light/dark
bevelGradient1.setColorAt(0.5, base);
}
p.setBrush(bevelGradient1);
p.drawEllipse(QRectF(3.4,3.4,7.2,7.2));
// inside mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush(QBrush(Qt::black));
p.drawEllipse(QRectF(4.0,4.0,6.0,6.0));
// shadow
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
drawInverseShadow(p, calcShadowColor(color), 4, 6, 0.5);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), size, size, size, size, size-1, size, 2, 1);
m_slabInvertedCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// slab
//---------------------------------------------------------
TileSet* StyleHelper::slab(const QColor &color, qreal shade, int size) const
{
Cache<TileSet>::Value *cache( m_slabCache.get(color) );
const quint64 key( ((int)(256.0 * shade)) << 24 | size );
TileSet *tileSet( cache->object( key ) );
if (!tileSet) {
QImage image(size*2, size*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(0,0,14,14);
// shadow
drawShadow(p, calcShadowColor(color), 14);
drawSlab(p, color, shade);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), size, size, size, size, size-1, size, 2, 1);
cache->insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// drawFloatFrame
//---------------------------------------------------------
void StyleHelper::drawFloatFrame(QPainter *p, const QRect r,
const QColor &color, bool drawUglyShadow, bool isActive,
const QColor &frameColor, TileSet::Tiles tiles) const
{
p->save();
p->setRenderHint(QPainter::Antialiasing);
const QRect frame( r.adjusted(1,1,-1,-1) );
int x,y,w,h;
frame.getRect(&x, &y, &w, &h);
QColor light( calcLightColor(backgroundTopColor(color)) );
QColor dark( calcLightColor(backgroundBottomColor(color)) );
p->setBrush(Qt::NoBrush);
if (drawUglyShadow) {
if (isActive) {
//window active - it's a glow - not a shadow
const QColor glow(ColorUtils::mix(QColor(128,128,128),frameColor,0.7) );
p->setPen(glow);
if (tiles & TileSet::Top) {
p->drawLine(QPointF(x+4, y-0.5), QPointF(x+w-4, y-0.5));
p->drawArc(QRectF(x-0.5, y-0.5, 11, 11),90*16, 90*16);
p->drawArc(QRectF(x+w-11+0.5, y-0.5, 11, 11), 0, 90*16);
}
if( tiles & TileSet::Left )
p->drawLine(QPointF(x-0.5, y+4), QPointF(x-0.5, y+h-4));
if( tiles & TileSet::Right )
p->drawLine(QPointF(x+w+0.5, y+4), QPointF(x+w+0.5, y+h-4));
if( tiles & TileSet::Bottom ) {
if( tiles & TileSet::Left )
p->drawArc(QRectF(x-0.5, y+h-11+0.5, 11, 11),180*16, 90*16);
if( tiles & TileSet::Right )
p->drawArc(QRectF(x+w-11+0.5, y+h-11+0.5, 11, 11),270*16, 90*16);
p->drawLine(QPointF(x+4, y+h+0.5), QPointF(x+w-4, y+h+0.5));
}
light = ColorUtils::mix(light, frameColor);
dark = ColorUtils::mix(dark, frameColor);
}
else {
// window inactive - draw something resembling shadow
// fully desaturate
const QColor shadow(ColorUtils::darken(color, 0.0, 0.0));
if (tiles & TileSet::Top) {
p->setPen(ColorUtils::darken(shadow, 0.2));
p->drawLine(QPointF(x+4, y-0.5), QPointF(x+w-4, y-0.5));
if (tiles & TileSet::Left)
p->drawArc(QRectF(x-0.5, y-0.5, 11, 11),90*16, 90*16);
if (tiles & TileSet::Right)
p->drawArc(QRectF(x+w-11+0.5, y-0.5, 11, 11), 0, 90*16);
}
p->setPen(ColorUtils::darken(shadow, 0.35));
if (tiles & TileSet::Left)
p->drawLine(QPointF(x-0.5, y+4), QPointF(x-0.5, y+h-4));
if (tiles & TileSet::Right)
p->drawLine(QPointF(x+w+0.5, y+4), QPointF(x+w+0.5, y+h-4));
if (tiles & TileSet::Bottom) {
p->setPen(ColorUtils::darken(shadow, 0.45));
if (tiles & TileSet::Left)
p->drawArc(QRectF(x-0.5, y+h-11+0.5, 11, 11),180*16, 90*16);
if (tiles & TileSet::Right)
p->drawArc(QRectF(x+w-11+0.5, y+h-11+0.5, 11, 11),270*16, 90*16);
p->setPen(ColorUtils::darken(shadow, 0.6));
p->drawLine(QPointF(x+4, y+h+0.5), QPointF(x+w-4, y+h+0.5));
}
}
}
// top frame
if (tiles & TileSet::Top) {
p->setPen(QPen(light, 0.8));
p->drawLine(QPointF(x+4, y+0.6), QPointF(x+w-4, y+0.6));
}
// corner and side frames
// sides are drawn even if Top only is selected, but with a different gradient
if (h >= 4+1.5) {
QLinearGradient lg(0.0, y+1.5, 0.0, y+h-4);
lg.setColorAt(0, light);
lg.setColorAt(1, alphaColor(light, 0) );
if (h > 20.5)
lg.setColorAt(qMax(0.0, 1.0 - 12.0/(h-5.5)), alphaColor(light, 0.5));
else if (h > 8.5)
lg.setColorAt(qMax(0.0, 3.0/(h-5.5)), alphaColor(light, 0.5));
p->setPen(QPen(lg, 0.8));
if (tiles & TileSet::Left )
p->drawLine(QPointF(x+0.6, y+4), QPointF(x+0.6, y+h-4));
if (tiles & TileSet::Right )
p->drawLine(QPointF(x+w-0.6, y+4), QPointF(x+w-0.6, y+h-4));
}
if (tiles & TileSet::Top ) {
const qreal offset = 0.5;
const qreal arc(7.0);
p->drawArc(QRectF(x+offset, y+offset, arc, arc),90*16, 90*16);
p->drawArc(QRectF(x+w-arc-offset, y+offset, arc, arc), 0, 90*16);
}
p->restore();
}
//---------------------------------------------------------
// roundCorner
//---------------------------------------------------------
TileSet* StyleHelper::roundCorner(const QColor &color, int size) const
{
const quint64 key( (quint64(color.rgba()) << 32)|size );
TileSet *tileSet = m_cornerCache.object(key);
if (!tileSet) {
QImage image(size*2, size*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
QLinearGradient lg = QLinearGradient(0.0, size-4.5, 0.0, size+4.5);
lg.setColorAt(0.0, calcLightColor( backgroundTopColor(color) ));
lg.setColorAt(0.51, backgroundBottomColor(color) );
lg.setColorAt(1.0, backgroundBottomColor(color) );
// draw ellipse.
p.setBrush( lg );
p.drawEllipse( QRectF( size-4, size-4, 8, 8 ) );
// mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush( Qt::black );
p.drawEllipse( QRectF( size-3, size-3, 6, 6 ) );
tileSet = new TileSet(QPixmap::fromImage(image), size, size, 1, 1);
m_cornerCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// slope
//---------------------------------------------------------
TileSet* StyleHelper::slope(const QColor &color, qreal shade, int size) const
{
const quint64 key((quint64(color.rgba()) << 32)|(quint64(256.0*shade)<<24)|size);
TileSet *tileSet = m_slopeCache.object(key);
if (!tileSet) {
QImage image(size*4, size*4, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setPen(Qt::NoPen);
// edges
TileSet *slabTileSet = slab(color, shade, size);
slabTileSet->render(QRect(0, 0, size*4, size*5), &p,
TileSet::Left | TileSet::Right | TileSet::Top);
p.setWindow(0,0,28,28);
// bottom
QColor light = ColorUtils::shade(calcLightColor(color), shade);
QLinearGradient fillGradient(0, -28, 0, 28);
light.setAlphaF(0.4); fillGradient.setColorAt(0.0, light);
light.setAlphaF(0.0); fillGradient.setColorAt(1.0, light);
p.setBrush(fillGradient);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.drawRect(3, 9, 22, 17);
// fade bottom
QLinearGradient maskGradient(0, 7, 0, 28);
maskGradient.setColorAt(0.0, Qt::black);
maskGradient.setColorAt(1.0, Qt::transparent);
p.setBrush(maskGradient);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawRect(0, 9, 28, 19);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), size, size, size*2, 2);
m_slopeCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// selection
//---------------------------------------------------------
TileSet *StyleHelper::selection(const QColor &color, int height, bool custom) const
{
const quint64 key( (quint64(color.rgba()) << 32) | (height << 1) | custom );
TileSet *tileSet = m_selectionCache.object(key);
if (!tileSet) {
const qreal rounding( 2.5 );
QImage image(32+16, height, QImage::Format_ARGB32_Premultiplied);
image.fill( Qt::transparent );
QRect r(image.rect().adjusted(0, 0, -1, -1));
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing);
p.translate(.5, .5);
{
// background
QPainterPath path;
path.addRoundedRect(r, rounding, rounding);
// items with custom background brushes always have their background drawn
// regardless of whether they are hovered or selected or neither so
// the gradient effect needs to be more subtle
const int lightenAmount( custom ? 110 : 130 );
QLinearGradient gradient(0, 0, 0, r.bottom());
gradient.setColorAt(0, color.lighter(lightenAmount));
gradient.setColorAt(1, color);
p.setPen(QPen(color, 1));
p.setBrush(gradient);
p.drawPath(path);
}
{
// contrast pixel
QPainterPath path;
path.addRoundedRect(r.adjusted(1, 1, -1, -1), rounding - 1, rounding - 1);
p.strokePath(path, QPen(QColor(255, 255, 255, 64), 1));
}
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), 8, 0, 32, height );
m_selectionCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// slitFocused
//---------------------------------------------------------
TileSet *StyleHelper::slitFocused(const QColor &glowColor) const
{
const quint64 key( (quint64(glowColor.rgba()) << 32) );
TileSet *tileSet = m_slitCache.object(key);
if (!tileSet) {
QImage image(9, 9, QImage::Format_ARGB32_Premultiplied);
QPainter p;
image.fill(Qt::transparent);
p.begin(&image);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
QRadialGradient rg = QRadialGradient(4.5, 4.5, 4.5, 4.5, 4.5);
QColor tmpColor = glowColor;
tmpColor.setAlpha(180*glowColor.alphaF());
rg.setColorAt(0.75, tmpColor);
tmpColor.setAlpha(0);
rg.setColorAt(0.90, tmpColor);
rg.setColorAt(0.4, tmpColor);
p.setBrush(rg);
p.drawEllipse(QRectF(0, 0, 9, 9));
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), 4, 4, 1, 1);
m_slitCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// drawSlab
//---------------------------------------------------------
void StyleHelper::drawSlab(QPainter &p, const QColor &color, qreal shade) const
{
const QColor light(ColorUtils::shade(calcLightColor(color), shade) );
const QColor base( alphaColor( light, 0.85 ) );
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade) );
// bevel, part 1
p.save();
const qreal y(ColorUtils::luma(base) );
const qreal yl(ColorUtils::luma(light) );
const qreal yd(ColorUtils::luma(dark) );
QLinearGradient bevelGradient1(0, 7, 0, 11);
bevelGradient1.setColorAt(0.0, light);
if (y < yl && y > yd) {
// no middle when color is very light/dark
bevelGradient1.setColorAt(0.5, base);
}
bevelGradient1.setColorAt(0.9, base);
p.setBrush(bevelGradient1);
p.drawRoundedRect(QRectF(3.0,3.0,8.0,8.0), 3.5, 3.5 );
// bevel, part 2
if (_slabThickness > 0.0) {
QLinearGradient bevelGradient2(0, 6, 0, 19);
bevelGradient2.setColorAt(0.0, light);
bevelGradient2.setColorAt(0.9, base);
p.setBrush(bevelGradient2);
p.drawEllipse(QRectF(3.6,3.6,6.8,6.8));
}
// inside mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush(Qt::black);
const qreal ic = 3.6 + 0.5*_slabThickness;
const qreal is = 6.8 - (2.0*0.5*_slabThickness);
p.drawEllipse(QRectF(ic, ic, is, is));
p.restore();
}
//---------------------------------------------------------
// drawShadow
//---------------------------------------------------------
void StyleHelper::drawShadow(QPainter &p, const QColor &color, int size) const
{
const qreal m( qreal(size-2)*0.5 );
const qreal offset( 0.8 );
const qreal k0( (m-4.0) / m );
QRadialGradient shadowGradient(m+1.0, m+offset+1.0, m);
for (int i = 0; i < 8; i++) {
// sinusoidal gradient
const qreal k1( (k0 * qreal(8 - i) + qreal(i)) * 0.125 );
const qreal a((cos(3.14159 * i * 0.125) + 1.0) * 0.30 );
shadowGradient.setColorAt(k1, alphaColor(color, a * _shadowGain));
}
shadowGradient.setColorAt(1.0, alphaColor(color, 0.0));
p.save();
p.setBrush(shadowGradient);
p.drawEllipse(QRectF(0, 0, size, size));
p.restore();
}
//---------------------------------------------------------
// drawOuterGlow
//---------------------------------------------------------
void StyleHelper::drawOuterGlow(QPainter &p, const QColor &color, int size) const
{
const QRectF r(.0, .0, size, size);
const qreal m = qreal(size) * 0.5;
const qreal width = 3.0;
const qreal bias(_glowBias * qreal(14)/size);
// k0 is located at width - bias from the outer edge
const qreal gm(m + bias - 0.9);
const qreal k0((m-width+bias) / gm);
QRadialGradient glowGradient(m, m, gm);
for (int i = 0; i < 8; i++) {
// k1 grows linearly from k0 to 1.0
const qreal k1( k0 + qreal(i)*(1.0-k0)/8.0 );
// a folows sqrt curve
const qreal a( 1.0 - sqrt(qreal(i)/8) );
glowGradient.setColorAt(k1, alphaColor(color, a));
}
// glow
p.save();
p.setBrush(glowGradient);
p.drawEllipse(r);
// inside mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush(Qt::black);
p.drawEllipse(r.adjusted(width, width, -width, -width));
p.restore();
}
//---------------------------------------------------------
// drawRoundSlab
//---------------------------------------------------------
void StyleHelper::drawRoundSlab(QPainter &p, const QColor &color, qreal shade) const
{
p.save();
// colors
const QColor base(ColorUtils::shade(color, shade) );
const QColor light(ColorUtils::shade(calcLightColor(color), shade) );
const QColor dark(ColorUtils::shade(calcDarkColor(color), shade) );
// bevel, part 1
QLinearGradient bevelGradient1(0, 10, 0, 18);
bevelGradient1.setColorAt(0.0, light);
bevelGradient1.setColorAt(0.9, alphaColor( light, 0.85 ) );
p.setBrush(bevelGradient1);
p.drawEllipse(QRectF(3.0,3.0,15.0,15.0));
// bevel, part 2
if (_slabThickness > 0.0) {
QLinearGradient bevelGradient2(0, 7, 0, 28);
bevelGradient2.setColorAt(0.0, light);
bevelGradient2.setColorAt(0.9, base);
p.setBrush(bevelGradient2);
p.drawEllipse(QRectF(3.6,3.6,13.8,13.8));
}
// inside
QLinearGradient innerGradient(0, -17, 0, 20);
innerGradient.setColorAt(0.0, light);
innerGradient.setColorAt(1.0, base);
p.setBrush(innerGradient);
const qreal ic( 3.6 + _slabThickness );
const qreal is( 13.8 - 2.0*_slabThickness );
p.drawEllipse(QRectF(ic, ic, is, is));
p.restore();
}
//---------------------------------------------------------
// groove
//---------------------------------------------------------
TileSet *StyleHelper::groove(const QColor &color, qreal shade, int size) const
{
const quint64 key((quint64(color.rgba()) << 32) | (quint64(256.0 * shade) << 24) | size);
TileSet *tileSet = m_grooveCache.object(key);
if (!tileSet) {
const int rsize((int)ceil(qreal(size) * 3.0/7.0));
QImage image(rsize*2, rsize*2, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setWindow(2,2,6,6);
// hole mask
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setBrush(Qt::black);
p.drawEllipse(4,4,2,2);
// shadow
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
drawInverseShadow(p, calcShadowColor(color), 3, 4, 0.0);
p.end();
tileSet = new TileSet(QPixmap::fromImage(image), rsize, rsize, rsize, rsize, rsize-1, rsize, 2, 1);
m_grooveCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// mergePalettes
//---------------------------------------------------------
QPalette StyleHelper::mergePalettes( const QPalette& source, qreal ratio ) const
{
QPalette out(source );
out.setColor(QPalette::Background, ColorUtils::mix( source.color( QPalette::Active, QPalette::Background ), source.color( QPalette::Disabled, QPalette::Background ), 1.0-ratio ) );
out.setColor(QPalette::Highlight, ColorUtils::mix( source.color( QPalette::Active, QPalette::Highlight ), source.color( QPalette::Disabled, QPalette::Highlight ), 1.0-ratio ) );
out.setColor(QPalette::WindowText, ColorUtils::mix( source.color( QPalette::Active, QPalette::WindowText ), source.color( QPalette::Disabled, QPalette::WindowText ), 1.0-ratio ) );
out.setColor(QPalette::ButtonText, ColorUtils::mix( source.color( QPalette::Active, QPalette::ButtonText ), source.color( QPalette::Disabled, QPalette::ButtonText ), 1.0-ratio ) );
out.setColor(QPalette::Text, ColorUtils::mix( source.color( QPalette::Active, QPalette::Text ), source.color( QPalette::Disabled, QPalette::Text ), 1.0-ratio ) );
out.setColor(QPalette::Button, ColorUtils::mix( source.color( QPalette::Active, QPalette::Button ), source.color( QPalette::Disabled, QPalette::Button ), 1.0-ratio ) );
return out;
}
//---------------------------------------------------------
// dockFrame
//---------------------------------------------------------
TileSet *StyleHelper::dockFrame(const QColor &color, int w)
{
const quint64 key( quint64(color.rgba()) << 32 | w );
TileSet *tileSet = m_dockFrameCache.object(key);
if (!tileSet) {
// width should be odd
if (!w&1)
--w;
// fixed height
const int h = 9;
QImage pm(w,h, QImage::Format_ARGB32_Premultiplied);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.save();
p.setRenderHints(QPainter::Antialiasing);
p.setBrush(Qt::NoBrush);
p.translate(0.5, 0.5);
QRect rect(0.5,0.5,w-0.5,h-0.);
QColor light = calcLightColor(color);
QColor dark = calcDarkColor(color);
{
// left and right border
QLinearGradient lg(QPoint(0,0), QPoint(w,0));
lg.setColorAt(0.0, alphaColor(light, 0.6));
lg.setColorAt(0.1, Qt::transparent);
lg.setColorAt(0.9, Qt::transparent);
lg.setColorAt(1.0, alphaColor(light, 0.6));
p.setPen(QPen(lg,1));
p.drawRoundedRect(rect.adjusted(0,-1,0,-1),4,5);
p.drawRoundedRect(rect.adjusted(2,1,-2,-2),4,5);
}
{
QLinearGradient lg(QPoint(0,0), QPoint(w,0));
lg.setColorAt(0.0, dark);
lg.setColorAt(0.1, Qt::transparent);
lg.setColorAt(0.9, Qt::transparent);
lg.setColorAt(1.0, dark);
p.setPen(QPen(lg, 1));
p.drawRoundedRect(rect.adjusted(1,0,-1,-2),4,5);
}
p.restore();
// top and bottom border
drawSeparator(&p, QRect(0,0,w,2), color, Qt::Horizontal);
drawSeparator(&p, QRect(0,h-2,w,2), color, Qt::Horizontal);
p.end();
tileSet = new TileSet(QPixmap::fromImage(pm), 4, 4, w-8, h-8);
m_dockFrameCache.insert(key, tileSet);
}
return tileSet;
}
//---------------------------------------------------------
// windecoButton
//---------------------------------------------------------
QPixmap StyleHelper::windecoButton(const QColor &color, bool pressed, int size) const
{
const quint64 key((quint64(color.rgba()) << 32) | (size << 1) | quint64(pressed));
QPixmap* pixmap = m_windecoButtonCache.object(key);
if (!pixmap) {
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
const QColor light(calcLightColor(color));
const QColor dark(calcDarkColor(color));
QPainter p(&image);
p.setRenderHints(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
const qreal u(size/18.0 );
p.translate(0.5*u, (0.5-0.668) * u);
{
// outline circle
qreal penWidth = 1.2;
QLinearGradient lg( 0, u*(1.665-penWidth), 0, u*(12.33+1.665-penWidth) );
lg.setColorAt( 0, dark );
lg.setColorAt( 1, light );
QRectF r( u*0.5*(17-12.33+penWidth), u*(1.665+penWidth), u*(12.33-penWidth), u*(12.33-penWidth) );
p.setPen( QPen( lg, penWidth*u ) );
p.drawEllipse( r );
p.end();
}
pixmap = new QPixmap(QPixmap::fromImage(image));
m_windecoButtonCache.insert(key, pixmap);
}
return *pixmap;
}