From 635c93bc5b1148c784574c02f4451a604a7b381c Mon Sep 17 00:00:00 2001 From: Hugo Pereira Da Costa Date: Fri, 18 Sep 2009 01:32:27 +0000 Subject: [PATCH] Added smooth transition between active and inactive state. This affects (when enabled) the window shadow, the horizontal separator line, the titlebar text color and the title+border window outline. svn path=/trunk/KDE/kdebase/workspace/; revision=1025111 --- clients/oxygen/oxygenbutton.cpp | 17 ++-- clients/oxygen/oxygenclient.cpp | 146 ++++++++++++++++++--------- clients/oxygen/oxygenclient.h | 41 +++++++- clients/oxygen/oxygenshadowcache.cpp | 57 +++++++++-- clients/oxygen/oxygenshadowcache.h | 8 +- 5 files changed, 203 insertions(+), 66 deletions(-) diff --git a/clients/oxygen/oxygenbutton.cpp b/clients/oxygen/oxygenbutton.cpp index 737ceb6716..6c5f6911ce 100644 --- a/clients/oxygen/oxygenbutton.cpp +++ b/clients/oxygen/oxygenbutton.cpp @@ -156,32 +156,29 @@ namespace Oxygen QPainter painter(this); painter.setClipRect(this->rect().intersected( event->rect() ) ); + painter.setRenderHints(QPainter::Antialiasing); QPalette palette = OxygenButton::palette(); if( client_.isActive() ) palette.setCurrentColorGroup(QPalette::Active); else palette.setCurrentColorGroup(QPalette::Inactive); + // window background client_.renderWindowBackground( &painter, rect(), this, palette ); - if( client_.isActive() && client_.configuration().drawTitleOutline() && !client_.isMaximized() ) + + // window border + if( client_.drawTitleOutline() && !client_.isMaximized() ) { client_.renderWindowBorder( &painter, rect(), this, client_.backgroundPalette( this, palette ) ); } - // draw dividing line - painter.setRenderHints(QPainter::Antialiasing); - QRect frame = client_.widget()->rect(); - int x = -this->geometry().x()+1; - int w = frame.width()-2; - - const int titleHeight = client_.layoutMetric(KCommonDecoration::LM_TitleHeight); + // colors QColor color = palette.window().color(); - QColor light = helper_.calcLightColor( color ); QColor dark = helper_.calcDarkColor( color ); dark.setAlpha(120); if( client_.drawSeparator() ) - { helper_.drawSeparator(&painter, QRect(x, titleHeight-1.5, w, 2), color, Qt::Horizontal); } + { client_.renderSeparator( &painter, rect(), this, color ); } // for menu button the application icon is used if (type_ == ButtonMenu) diff --git a/clients/oxygen/oxygenclient.cpp b/clients/oxygen/oxygenclient.cpp index 7a4c93ac99..0184ef55b7 100644 --- a/clients/oxygen/oxygenclient.cpp +++ b/clients/oxygen/oxygenclient.cpp @@ -50,8 +50,9 @@ using namespace std; namespace Oxygen { + const int maxAnimationIndex( 256 ); K_GLOBAL_STATIC_WITH_ARGS(OxygenHelper, globalHelper, ("oxygenDeco")) - K_GLOBAL_STATIC_WITH_ARGS(OxygenShadowCache, globalShadowCache, (512)) + K_GLOBAL_STATIC_WITH_ARGS(OxygenShadowCache, globalShadowCache, (maxAnimationIndex)) //___________________________________________ OxygenHelper *oxygenHelper() @@ -81,6 +82,7 @@ namespace Oxygen KCommonDecorationUnstable(b, f), colorCacheInvalid_(true), sizeGrip_( 0 ), + timeLine_( 200, this ), helper_(*globalHelper), initialized_( false ) { qAddPostRoutine(oxkwincleanupBefore); } @@ -105,6 +107,13 @@ namespace Oxygen KCommonDecoration::init(); widget()->setAttribute(Qt::WA_NoSystemBackground ); widget()->setAutoFillBackground( false ); + + // initialize timeLine + timeLine_.setFrameRange( 0, maxAnimationIndex ); + timeLine_.setCurveShape( QTimeLine::EaseInOutCurve ); + connect( &timeLine_, SIGNAL( frameChanged( int ) ), widget(), SLOT( update() ) ); + connect( &timeLine_, SIGNAL( finished() ), widget(), SLOT( update() ) ); + initialized_ = true; // in case of preview, one wants to make the label used @@ -184,10 +193,6 @@ namespace Oxygen // make resizing easier border = qMax(frameBorder, 4); - } else if( configuration().frameBorder() == OxygenConfiguration::BorderNone && isPreview() && !compositingActive() ) { - - border = 1; - } else if( frameBorder < OxygenConfiguration::BorderTiny ) { border = 0; @@ -379,7 +384,14 @@ namespace Oxygen if( configuration().drawTitleOutline() ) { - return options()->color(ColorFont, isActive()); + if( timeLineIsRunning() ) + { + + return KColorUtils::mix( + options()->color(ColorFont, false), + options()->color(ColorFont, true ), opacity() ); + + } else return options()->color(ColorFont, isActive()); } else if (isActive()) { @@ -443,9 +455,8 @@ namespace Oxygen // save painter painter->save(); - if (clipRect.isValid()) { - painter->setClipRegion(clipRect,Qt::IntersectClip); - } + if( timeLineIsRunning() ) painter->setOpacity( opacity() ); + if( clipRect.isValid() ) painter->setClipRegion(clipRect,Qt::IntersectClip); QRect r = (isPreview()) ? OxygenClient::widget()->rect():window->rect(); qreal shadowSize( oxygenShadowCache()->shadowSize() ); @@ -506,10 +517,59 @@ namespace Oxygen } + //_________________________________________________________ + void OxygenClient::renderSeparator( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QColor& color ) const + { + + const QWidget* window = (isPreview()) ? OxygenClient::widget() : widget->window(); + + // get coordinates relative to the client area + // this is annoying. One could use mapTo if this was taking const QWidget* and not + // const QWidget* as argument. + QPoint position( 0, 0 ); + { + const QWidget* w = widget; + while ( w != window && !w->isWindow() && w != w->parentWidget() ) { + position += w->geometry().topLeft(); + w = w->parentWidget(); + } + } + + // setup painter + painter->save(); + if( timeLineIsRunning() ) painter->setOpacity( opacity() ); + if (clipRect.isValid()) painter->setClipRegion(clipRect,Qt::IntersectClip); + + QRect r = (isPreview()) ? OxygenClient::widget()->rect():window->rect(); + qreal shadowSize( oxygenShadowCache()->shadowSize() ); + r.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize ); + r.adjust(0,0, 1, 1); + + int extraBorder = ( isMaximized() && compositingActive() ) ? 0 : EXTENDED_HITAREA; + + // dimensions + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleTop = layoutMetric(LM_TitleEdgeTop) + r.top() - extraBorder; + + // dimensions + int x,y,w,h; + r.getRect(&x, &y, &w, &h); + helper().drawSeparator( painter, QRect(x, titleTop+titleHeight-1.5, w, 2).translated( -position ), color, Qt::Horizontal); + + painter->restore(); + + } + //_________________________________________________________ void OxygenClient::renderTitleOutline( QPainter* painter, const QRect& rect, const QPalette& palette ) const { + if( timeLineIsRunning() ) + { + painter->save(); + painter->setOpacity( opacity() ); + } + // shadow { int shadowSize = 7; @@ -524,6 +584,8 @@ namespace Oxygen renderWindowBackground(painter, rect.adjusted( 4, voffset, -4, -4 ), widget(), palette ); } + if( timeLineIsRunning() ) painter->restore(); + } //_________________________________________________________ @@ -537,6 +599,14 @@ namespace Oxygen sizeGrip().update(); } + // reset animation + if( animateActiveChange() ) + { + timeLine_.setDirection( isActive() ? QTimeLine::Forward : QTimeLine::Backward ); + if(timeLine_.state() == QTimeLine::NotRunning ) + { timeLine_.start(); } + } + KCommonDecorationUnstable::activeChange(); } @@ -627,9 +697,20 @@ namespace Oxygen if( compositingActive() && !isMaximized() ) { - oxygenShadowCache()->tileSet( this )->render( - frame.adjusted( 4, 4, -4, -4), - &painter, TileSet::Ring); + if( configuration().useOxygenShadows() && timeLineIsRunning() ) + { + + oxygenShadowCache()->tileSet( this, timeLine_.currentFrame() )->render( + frame.adjusted( 4, 4, -4, -4), + &painter, TileSet::Ring); + + } else { + + oxygenShadowCache()->tileSet( this )->render( + frame.adjusted( 4, 4, -4, -4), + &painter, TileSet::Ring); + + } } @@ -678,38 +759,11 @@ namespace Oxygen // window background renderWindowBackground( &painter, frame, widget(), palette ); - if( isActive() && configuration().drawTitleOutline() && !isMaximized() ) - { - renderWindowBorder( &painter, frame, widget(), backgroundPalette( widget(), palette ) ); - } + if( drawTitleOutline() && !isMaximized() ) renderWindowBorder( &painter, frame, widget(), backgroundPalette( widget(), palette ) ); // clipping if( compositingActive() ) painter.setClipping(false); - // in preview mode and when frame border is 0, - // one still draw a small rect around, unless kde is recent enough, - // useOxygenShadow is set to true, - // and copositing is active - // (that makes a lot of ifs) - if( isPreview() && configuration().frameBorder() == OxygenConfiguration::BorderNone && !compositingActive() ) - { - painter.save(); - painter.setBrush( Qt::NoBrush ); - painter.setPen( QPen( helper().calcDarkColor( widget()->palette().window().color() ), 1 ) ); - - QPainterPath path; - QPoint first( frame.topLeft()+QPoint( 0, 6 ) ); - path.moveTo( first ); - path.quadTo( frame.topLeft(), frame.topLeft()+QPoint( 6, 0 ) ); - path.lineTo( frame.topRight()-QPoint( 6, 0 ) ); - path.quadTo( frame.topRight(), frame.topRight()+QPoint( 0, 6 ) ); - path.lineTo( frame.bottomRight() ); - path.lineTo( frame.bottomLeft() ); - path.lineTo( first ); - painter.drawPath( path ); - painter.restore(); - } - int extraBorder = ( isMaximized() && compositingActive() ) ? 0 : EXTENDED_HITAREA; // dimensions @@ -728,7 +782,7 @@ namespace Oxygen QRect titleRect( titleLeft, titleTop-1, titleWidth, titleHeight ); painter.setFont( options()->font(isActive(), false) ); - if( isActive() && configuration().drawTitleOutline() ) + if( drawTitleOutline() ) { // get title bounding rect @@ -739,11 +793,13 @@ namespace Oxygen boundingRect.setBottom( titleTop+titleHeight ); boundingRect.setLeft( qMax( boundingRect.left(), titleLeft ) - 2*HFRAMESIZE ); boundingRect.setRight( qMin( boundingRect.right(), titleLeft + titleWidth ) + 2*HFRAMESIZE ); - renderTitleOutline( &painter, boundingRect, backgroundPalette( widget(), palette ) ); } + // separator + if( drawSeparator() ) renderSeparator(&painter, frame, widget(), color ); + // draw title text painter.setPen( titlebarTextColor( backgroundPalette( widget(), palette ) ) ); @@ -751,16 +807,12 @@ namespace Oxygen painter.setRenderHint(QPainter::Antialiasing); // adjust if there are shadows - if (compositingActive()) frame.adjust(-1,-1, 1, 1); + if( compositingActive() ) frame.adjust(-1,-1, 1, 1); // dimensions int x,y,w,h; frame.getRect(&x, &y, &w, &h); - // separator - if( drawSeparator() ) - { helper().drawSeparator(&painter, QRect(x, titleTop+titleHeight-1.5, w, 2), color, Qt::Horizontal); } - // shadow and resize handles if( configuration().frameBorder() >= OxygenConfiguration::BorderTiny && !isMaximized() ) { diff --git a/clients/oxygen/oxygenclient.h b/clients/oxygen/oxygenclient.h index a1d4bdfb7c..34b5cc89a4 100644 --- a/clients/oxygen/oxygenclient.h +++ b/clients/oxygen/oxygenclient.h @@ -29,6 +29,7 @@ ////////////////////////////////////////////////////////////////////////////// #include +#include #include "oxygenconfiguration.h" #include "lib/helper.h" @@ -60,9 +61,22 @@ namespace Oxygen //! true if window is maximized virtual bool isMaximized( void ) const; + //! true when title outline is to be drawn + bool drawTitleOutline( void ) const + { + return + ( timeLineIsRunning() || isActive() ) && + configuration().drawTitleOutline(); + } + //! true when separator is to be drawn - virtual bool drawSeparator( void ) const - { return isActive() && configuration().drawSeparator() && !configuration().drawTitleOutline(); } + bool drawSeparator( void ) const + { + return + ( timeLineIsRunning() || isActive() ) && + configuration().drawSeparator() && + !configuration().drawTitleOutline(); + } //! dimensions virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; @@ -93,6 +107,9 @@ namespace Oxygen // this draws a "blue" border around active window virtual void renderWindowBorder( QPainter*, const QRect&, const QWidget*, const QPalette& ) const; + //! separator + virtual void renderSeparator( QPainter*, const QRect&, const QWidget*, const QColor& ) const; + //! title outline virtual void renderTitleOutline( QPainter*, const QRect&, const QPalette& ) const; @@ -117,6 +134,23 @@ namespace Oxygen private: + //! return true when activity change are animated + bool animateActiveChange( void ) const + { + return + configuration().useOxygenShadows() || + configuration().drawSeparator() || + configuration().drawTitleOutline(); + } + + //! return true if timeLine is running + bool timeLineIsRunning( void ) const + { return timeLine_.state() == QTimeLine::Running; } + + //! return animation opacity + qreal opacity( void ) const + { return qreal( timeLine_.currentFrame() )/qreal( timeLine_.endFrame() ); } + //! calculate mask QRegion calcMask( void ) const; @@ -154,6 +188,9 @@ namespace Oxygen //! size grip widget OxygenSizeGrip* sizeGrip_; + //! animation timeLine + QTimeLine timeLine_; + //! helper OxygenHelper& helper_; diff --git a/clients/oxygen/oxygenshadowcache.cpp b/clients/oxygen/oxygenshadowcache.cpp index 73806980a2..e869cd6e19 100644 --- a/clients/oxygen/oxygenshadowcache.cpp +++ b/clients/oxygen/oxygenshadowcache.cpp @@ -27,12 +27,25 @@ #include "oxygenshadowcache.h" #include "oxygenclient.h" +#include #include #include namespace Oxygen { + + //_______________________________________________________ + OxygenShadowCache::OxygenShadowCache( int maxIndex ): + maxIndex_( maxIndex ), + activeShadowConfiguration_( OxygenShadowConfiguration( QPalette::Active ) ), + inactiveShadowConfiguration_( OxygenShadowConfiguration( QPalette::Inactive ) ) + { + kDebug(1212) << endl; + shadowCache_.setMaxCost( 1<<5 ); + animatedShadowCache_.setMaxCost( maxIndex_<<5 ); + } + //_______________________________________________________ bool OxygenShadowCache::shadowConfigurationChanged( const OxygenShadowConfiguration& other ) const { @@ -72,13 +85,44 @@ namespace Oxygen } //_______________________________________________________ - OxygenShadowCache::OxygenShadowCache( int maxIndex ): - maxIndex_( maxIndex ), - activeShadowConfiguration_( OxygenShadowConfiguration( QPalette::Active ) ), - inactiveShadowConfiguration_( OxygenShadowConfiguration( QPalette::Inactive ) ) + TileSet* OxygenShadowCache::tileSet( const OxygenClient* client, int index ) { - kDebug(1212) << endl; - shadowCache_.setMaxCost( 1<<5 ); + + assert( index <= maxIndex_ ); + + // construct key + Key key = Key(); + key.index = index; + key.active = client->isActive(); + key.useOxygenShadows = client->configuration().useOxygenShadows(); + key.isShade = client->isShade(); + key.hasNoBorder = client->configuration().frameBorder() == OxygenConfiguration::BorderNone; + key.hasTitleOutline = client->configuration().drawTitleOutline(); + + // 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 ); + + p.setOpacity( 1.0 - opacity ); + p.drawPixmap( QPointF(0,0), shadowPixmap( client, false ) ); + + p.setOpacity( opacity ); + p.drawPixmap( QPointF(0,0), shadowPixmap( client, true ) ); + p.end(); + + TileSet* tileSet = new TileSet(shadow, size, size, 1, 1); + animatedShadowCache_.insert( hash, tileSet ); + return tileSet; + } //_________________________________________________________________ @@ -313,6 +357,7 @@ namespace Oxygen // note this can be optimized because not all of the flag configurations are actually relevant // allocate 3 empty bits for flags int out = + ( index << 5 ) | ( active << 4 ) | (useOxygenShadows << 3 ) | (isShade<<2) | diff --git a/clients/oxygen/oxygenshadowcache.h b/clients/oxygen/oxygenshadowcache.h index e680ab2493..be209faf1e 100644 --- a/clients/oxygen/oxygenshadowcache.h +++ b/clients/oxygen/oxygenshadowcache.h @@ -52,6 +52,7 @@ namespace Oxygen void invalidateCaches( void ) { shadowCache_.clear(); + animatedShadowCache_.clear(); } //! returns true if provided shadow configuration changes with respect to stored @@ -86,6 +87,7 @@ namespace Oxygen //! explicit constructor explicit Key( void ): + index(0), active(false), useOxygenShadows(false), isShade(false), @@ -96,6 +98,7 @@ namespace Oxygen //! hash function int hash( void ) const; + int index; bool active; bool useOxygenShadows; bool isShade; @@ -127,9 +130,12 @@ namespace Oxygen //! cache typedef QCache TileSetCache; - //! active shadow cache + //! shadow cache TileSetCache shadowCache_; + //! animated shadow cache + TileSetCache animatedShadowCache_; + }; }