////////////////////////////////////////////////////////////////////////////// // oxygenshadowcache.cpp // handles caching of TileSet objects to draw shadows // ------------------- // // Copyright (c) 2009 Hugo Pereira Da Costa // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////// #include "oxygenshadowcache.h" #include "oxygenclient.h" #include "oxygen.h" #include "lib/helper.h" #include #include #include #include namespace Oxygen { //_______________________________________________________ OxygenShadowCache::OxygenShadowCache( OxygenHelper& helper, int maxIndex ): helper_( helper ), maxIndex_( maxIndex ), activeShadowConfiguration_( OxygenShadowConfiguration( QPalette::Active ) ), inactiveShadowConfiguration_( OxygenShadowConfiguration( QPalette::Inactive ) ) { kDebug(1212) << endl; shadowCache_.setMaxCost( 1<<6 ); animatedShadowCache_.setMaxCost( maxIndex_<<6 ); } //_______________________________________________________ bool OxygenShadowCache::shadowConfigurationChanged( const OxygenShadowConfiguration& other ) const { const OxygenShadowConfiguration& local = (other.colorGroup() == QPalette::Active ) ? activeShadowConfiguration_:inactiveShadowConfiguration_; return !(local == other); } //_______________________________________________________ void OxygenShadowCache::setShadowConfiguration( const OxygenShadowConfiguration& other ) { OxygenShadowConfiguration& local = (other.colorGroup() == QPalette::Active ) ? activeShadowConfiguration_:inactiveShadowConfiguration_; local = other; } //_______________________________________________________ TileSet* OxygenShadowCache::tileSet( const OxygenClient* client ) { // construct key Key key( client ); // check if tileset already in cache int hash( key.hash() ); if( shadowCache_.contains(hash) ) return shadowCache_.object(hash); // create tileset otherwise qreal size( shadowSize() ); TileSet* tileSet = new TileSet( shadowPixmap( client, key.active ), size, size, 1, 1); shadowCache_.insert( hash, tileSet ); return tileSet; } //_______________________________________________________ TileSet* OxygenShadowCache::tileSet( const OxygenClient* client, int index ) { assert( index <= maxIndex_ ); // construct key Key key( client ); key.index = index; // check if tileset already in cache int hash( key.hash() ); if( animatedShadowCache_.contains(hash) ) return animatedShadowCache_.object(hash); // create shadow and tileset otherwise qreal size( shadowSize() ); qreal opacity( qreal(index)/qreal(maxIndex_) ); QPixmap shadow( size*2, size*2 ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); QPixmap inactiveShadow( shadowPixmap( client, false ) ); { QPainter pp( &inactiveShadow ); pp.setRenderHint( QPainter::Antialiasing ); pp.setCompositionMode(QPainter::CompositionMode_DestinationIn); pp.fillRect( inactiveShadow.rect(), QColor( 0, 0, 0, 255*(1.0-opacity ) ) ); } QPixmap activeShadow( shadowPixmap( client, true ) ); { QPainter pp( &activeShadow ); pp.setRenderHint( QPainter::Antialiasing ); pp.setCompositionMode(QPainter::CompositionMode_DestinationIn); pp.fillRect( activeShadow.rect(), QColor( 0, 0, 0, 255*( opacity ) ) ); } p.drawPixmap( QPointF(0,0), inactiveShadow ); p.drawPixmap( QPointF(0,0), activeShadow ); p.end(); TileSet* tileSet = new TileSet(shadow, size, size, 1, 1); animatedShadowCache_.insert( hash, tileSet ); return tileSet; } //_________________________________________________________________ QPixmap OxygenShadowCache::shadowPixmap(const OxygenClient* client, bool active ) const { // get window color Key key( client ); QPalette palette( client->backgroundPalette( client->widget(), client->widget()->palette() ) ); QColor color( palette.color( client->widget()->backgroundRole() ) ); if( active && client->configuration().drawTitleOutline() && client->configuration().frameBorder() == OxygenConfiguration::BorderNone ) { // a more complex tile set is needed for the configuration above: // top corners must be beveled with the "active titlebar color" while // bottom corners must be beveled with the "window background color". // this requires generating two shadow pixmaps and tiling them in the tileSet. qreal size( shadowSize() ); QPixmap shadow( 2*size, 2*size ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); QPixmap shadowTop = simpleShadowPixmap( color, key, active ); QRect topRect( shadow.rect() ); topRect.setBottom( int( size )-1 ); p.setClipRect( topRect ); p.drawPixmap( QPointF( 0, 0 ), shadowTop ); // get window color palette = client->widget()->palette(); color = palette.color( client->widget()->backgroundRole() ); QPixmap shadowBottom = simpleShadowPixmap( color, key, active ); QRect bottomRect( shadow.rect() ); bottomRect.setTop( int( size ) ); p.setClipRect( bottomRect ); p.drawPixmap( QPointF( 0, 0 ), shadowBottom ); p.end(); return shadow; } else return simpleShadowPixmap( color, key, active ); } //_______________________________________________________ QPixmap OxygenShadowCache::simpleShadowPixmap( const QColor& color, const Key& key, bool active ) const { // local reference to relevant shadow configuration const OxygenShadowConfiguration& shadowConfiguration( active && key.useOxygenShadows ? activeShadowConfiguration_:inactiveShadowConfiguration_ ); static const qreal fixedSize = 25.5; qreal size( shadowSize() ); qreal shadowSize( shadowConfiguration.shadowSize() ); QPixmap shadow = QPixmap( size*2, size*2 ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); p.setPen( Qt::NoPen ); // offsets are scaled with the shadow size // so that the ratio Top-shadow/Bottom-shadow is kept constant // when shadow size is changed qreal hoffset = shadowConfiguration.horizontalOffset()*shadowSize/fixedSize; qreal voffset = shadowConfiguration.verticalOffset()*shadowSize/fixedSize; // some gradients rendering are different at bottom corners for NoBorders windows bool noBorder( key.frameBorder == Key::BorderNone && !key.isShade ); if( active && key.useOxygenShadows ) { { // inner (shark) gradient int nPoints = 7; qreal x[7] = {0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4 }; int values[7] = {203, 200, 175, 105, 45, 2, 0 }; QRadialGradient rg = QRadialGradient( size+12.0*hoffset, size+12.0*voffset, shadowSize ); QColor c = shadowConfiguration.innerColor(); for( int i = 0; iisActive(); useOxygenShadows = client->configuration().useOxygenShadows(); isShade = client->isShade(); hasTitleOutline = client->configuration().drawTitleOutline(); switch( client->configuration().frameBorder() ) { case OxygenConfiguration::BorderNone: frameBorder = Key::BorderNone; break; case OxygenConfiguration::BorderNoSide: frameBorder = Key::BorderNoSide; break; default: frameBorder = Key::BorderAny; break; } } }