Moved title transitin handling to separate class (oxygentitleanimationdata).

Use blended pixmaps to do the transition instead of QPainter::drawText with alpha blended colors.
This fixes glitches when title is changed while animation is running. (thanks to Wilder for reporting). 


svn path=/trunk/KDE/kdebase/workspace/; revision=1063441
This commit is contained in:
Hugo Pereira Da Costa 2009-12-18 15:20:04 +00:00
parent 223f1f2f0b
commit 80b5f81f72
5 changed files with 428 additions and 94 deletions

View file

@ -17,6 +17,7 @@ set(kwin_oxygen_SRCS
oxygenshadowconfiguration.cpp
oxygenshadowcache.cpp
oxygensizegrip.cpp
oxygentitleanimationdata.cpp
)
kde4_add_plugin(kwin3_oxygen ${kwin_oxygen_SRCS})

View file

@ -84,9 +84,8 @@ namespace Oxygen
factory_( f ),
sizeGrip_( 0 ),
glowAnimation_( new Animation( 200, this ) ),
titleAnimation_( new Animation( 200, this ) ),
titleAnimationData_( new TitleAnimationData( this ) ),
glowIntensity_(0),
titleOpacity_(0),
initialized_( false ),
forceActive_( false ),
mouseButton_( Qt::NoButton ),
@ -126,15 +125,10 @@ namespace Oxygen
connect( glowAnimation().data(), SIGNAL( finished( void ) ), widget(), SLOT( update( void ) ) );
connect( glowAnimation().data(), SIGNAL( finished() ), this, SLOT( clearForceActive() ) );
// setup title animation
titleAnimation().data()->setStartValue( 0 );
titleAnimation().data()->setEndValue( 1 );
titleAnimation().data()->setTargetObject( this );
titleAnimation().data()->setPropertyName( "titleOpacity" );
titleAnimation().data()->setCurveShape( Animation::EaseInOutCurve );
connect( titleAnimation().data(), SIGNAL( valueChanged( const QVariant& ) ), widget(), SLOT( update( void ) ) );
connect( titleAnimation().data(), SIGNAL( finished( void ) ), widget(), SLOT( update( void ) ) );
connect( titleAnimation().data(), SIGNAL( finished( void ) ), this, SLOT( updateOldCaption() ) );
// title animation data
titleAnimationData_.data()->initialize();
connect( titleAnimationData_.data(), SIGNAL( pixmapsChanged() ), widget(), SLOT( update( void ) ) );
// lists
connect( itemData_.animation().data(), SIGNAL( finished() ), this, SLOT( clearTargetItem() ) );
@ -177,9 +171,12 @@ namespace Oxygen
// animations duration
glowAnimation().data()->setDuration( configuration_.animationsDuration() );
titleAnimation().data()->setDuration( configuration_.animationsDuration() );
titleAnimationData_.data()->setDuration( configuration_.animationsDuration() );
itemData_.animation().data()->setDuration( configuration_.animationsDuration() );
// reset title transitions
titleAnimationData_.data()->reset();
// should also update animations for buttons
resetButtons();
@ -190,9 +187,6 @@ namespace Oxygen
if( item.closeButton_ ) { item.closeButton_.data()->reset(0); }
}
// copy current caption to old
updateOldCaption();
// reset tab geometry
itemData_.setDirty( true );
@ -814,51 +808,41 @@ namespace Oxygen
void OxygenClient::renderTitleText( QPainter* painter, const QRect& rect, const QColor& color, const QColor& contrast ) const
{
if( titleIsAnimated() )
if( !titleAnimationData_.data()->isValid() )
{
// contrast pixmap
titleAnimationData_.data()->reset(
rect,
renderTitleText( rect, caption(), color ),
renderTitleText( rect, caption(), contrast ) );
}
if( titleAnimationData_.data()->isDirty() )
{
// due to alpha blending issues, one must first draw the contrast text,
// then the plain text.
if( contrast.isValid() )
{
// contrast pixmap
titleAnimationData_.data()->setPixmaps(
rect,
renderTitleText( rect, caption(), color ),
renderTitleText( rect, caption(), contrast ) );
painter->translate( 0, 1 );
if( !oldCaption().isEmpty() ) {
titleAnimationData_.data()->setDirty( false );
titleAnimationData_.data()->startAnimation();
renderTitleText( painter, rect, color, contrast );
renderTitleText(
painter, rect, oldCaption(),
helper().alphaColor( contrast, 1.0 - titleOpacity() ),
QColor(), false );
} else if( titleAnimationData_.data()->isAnimated() ) {
if( isMaximized() ) painter->translate( 0, 2 );
if( !titleAnimationData_.data()->contrastPixmap().isNull() )
{
painter->translate( 0, 1 );
painter->drawPixmap( rect.topLeft(), titleAnimationData_.data()->contrastPixmap() );
painter->translate( 0, -1 );
}
if( !caption().isEmpty() ) {
painter->drawPixmap( rect.topLeft(), titleAnimationData_.data()->pixmap() );
renderTitleText(
painter, rect, caption(),
helper().alphaColor( contrast, titleOpacity() ), QColor() );
}
painter->translate( 0, -1 );
}
if( !oldCaption().isEmpty() ) {
renderTitleText(
painter, rect, oldCaption(),
helper().alphaColor( color, 1.0 - titleOpacity() ), QColor(), false );
}
if( !caption().isEmpty() ) {
renderTitleText(
painter, rect, caption(),
helper().alphaColor( color, titleOpacity() ), QColor() );
}
if( isMaximized() ) painter->translate( 0, -2 );
} else if( !caption().isEmpty() ) {
@ -894,6 +878,26 @@ namespace Oxygen
}
//_______________________________________________________________________
QPixmap OxygenClient::renderTitleText( const QRect& rect, const QString& caption, const QColor& color, bool elide ) const
{
QPixmap out( rect.size() );
out.fill( Qt::transparent );
if( caption.isEmpty() || !color.isValid() ) return out;
QPainter painter( &out );
painter.setFont( options()->font(isActive(), false) );
Qt::Alignment alignment( configuration().titleAlignment() | Qt::AlignVCenter );
QString local( elide ? QFontMetrics( painter.font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption );
painter.setPen( color );
painter.drawText( out.rect(), alignment, local );
painter.end();
return out;
}
//_______________________________________________________________________
void OxygenClient::renderItem( QPainter* painter, int index, const QPalette& palette )
{
@ -1154,10 +1158,11 @@ namespace Oxygen
//_________________________________________________________
void OxygenClient::captionChange( void )
{
KCommonDecorationUnstable::captionChange();
itemData_.setDirty( true );
if( !animateTitleChange() ) return;
titleAnimation().data()->restart();
if( animateTitleChange() )
{ titleAnimationData_.data()->setDirty( true ); }
}
@ -1495,6 +1500,8 @@ namespace Oxygen
int itemClicked( OxygenClient::itemClicked( point ) );
if( itemClicked < 0 ) return false;
titleAnimationData_.data()->reset();
QDrag *drag = new QDrag( widget() );
QMimeData *groupData = new QMimeData();
groupData->setData( clientGroupItemDragMimeType(), QString().setNum( itemId( itemClicked )).toAscii() );
@ -1660,6 +1667,7 @@ namespace Oxygen
}
titleAnimationData_.data()->reset();
return true;
}

View file

@ -31,6 +31,7 @@
#include "oxygen.h"
#include "oxygenclientgroupitemdata.h"
#include "oxygenconfiguration.h"
#include "oxygentitleanimationdata.h"
#include "lib/oxygenanimation.h"
#include "lib/helper.h"
@ -51,9 +52,6 @@ namespace Oxygen
//!Q_PROPERTY( qreal glowIntensity READ glowIntensity WRITE setGlowIntensity )
Q_PROPERTY( qreal glowIntensity READ glowIntensityUnbiased WRITE setGlowIntensity )
//! declare title opacity
Q_PROPERTY( qreal titleOpacity READ titleOpacity WRITE setTitleOpacity )
public:
//! constructor
@ -262,6 +260,9 @@ namespace Oxygen
/*! second color, if valid, is for contrast pixel */
virtual void renderTitleText( QPainter*, const QRect&, const QString&, const QColor&, const QColor& = QColor(), bool elide = true ) const;
//! title text
virtual QPixmap renderTitleText( const QRect&, const QString&, const QColor&, bool elide = true ) const;
//! GroupItem
virtual void renderItem( QPainter*, int, const QPalette& );
@ -286,31 +287,6 @@ namespace Oxygen
//! return pixmap corresponding to a given tab, for dragging
QPixmap itemDragPixmap( int, const QRect& );
//!@name title animation
//@{
const Animation::Pointer& titleAnimation( void ) const
{ return titleAnimation_; }
bool titleIsAnimated( void ) const
{ return titleAnimation().data()->isRunning(); }
qreal titleOpacity( void ) const
{ return titleOpacity_; }
void setTitleOpacity( qreal value )
{ titleOpacity_ = value; }
//@}
//! old caption if any
const QString& oldCaption( void ) const
{ return oldCaption_; }
//! old caption
void setOldCaption( const QString& value )
{ oldCaption_ = value; }
//! return true when activity change are animated
bool animateActiveChange( void ) const
{ return ( configuration().useAnimations() && !isPreview() ); }
@ -363,10 +339,6 @@ namespace Oxygen
protected slots:
//! update old caption with current
void updateOldCaption( void )
{ setOldCaption( caption() ); }
//! set target item to -1
void clearTargetItem( void );
@ -392,18 +364,12 @@ namespace Oxygen
//! glow animation
Animation::Pointer glowAnimation_;
//! title animation
Animation::Pointer titleAnimation_;
//! title animation data
TitleAnimationData::Pointer titleAnimationData_;
//! glow intensity
qreal glowIntensity_;
//! title opacity
qreal titleOpacity_;
//! old caption
QString oldCaption_;
//! true when initialized
bool initialized_;

View file

@ -0,0 +1,133 @@
//////////////////////////////////////////////////////////////////////////////
// oxygentitleanimationdata.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
// Copyright (c) 2003, 2004 David Johnson <david@usermode.org>
// Copyright (c) 2006, 2007 Riccardo Iaconelli <ruphy@fsfe.org>
//
// 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 "oxygentitleanimationdata.h"
#include "oxygentitleanimationdata.moc"
#include <QtGui/QPainter>
namespace Oxygen
{
//_________________________________________________________
TitleAnimationData::TitleAnimationData( QObject* parent ):
QObject( parent ),
dirty_( false ),
animation_( new Animation( 200, this ) ),
opacity_(0)
{}
//_________________________________________________________
void TitleAnimationData::initialize( void )
{
// setup title animation
animation().data()->setStartValue( 0 );
animation().data()->setEndValue( 1 );
animation().data()->setTargetObject( this );
animation().data()->setPropertyName( "opacity" );
animation().data()->setCurveShape( Animation::EaseInOutCurve );
connect( animation().data(), SIGNAL( valueChanged( const QVariant& ) ), SLOT( updatePixmaps( void ) ) );
connect( animation().data(), SIGNAL( finished( void ) ), SLOT( updatePixmaps( void ) ) );
}
//_________________________________________________________
void TitleAnimationData::setPixmaps( QRect rect, QPixmap pixmap, QPixmap contrast )
{
// stop animation
if( isAnimated() ) animation().data()->stop();
// update pixmaps
contrastPixmap_.initialize( rect, contrast );
pixmap_.initialize( rect, pixmap );
setOpacity(0);
updatePixmaps();
}
//_________________________________________________________
void TitleAnimationData::updatePixmaps( void )
{
contrastPixmap_.blend( opacity() );
pixmap_.blend( opacity() );
emit pixmapsChanged();
}
//_________________________________________________________
void TitleAnimationData::BlendedPixmap::blend( qreal opacity )
{
currentPixmap_ = QPixmap( endRect_.size() );
currentPixmap_.fill( Qt::transparent );
QPainter painter( &currentPixmap_ );
if( opacity < 1 && !startPixmap_.isNull() )
{ painter.drawPixmap( startRect_.topLeft() - endRect_.topLeft(), fade( startPixmap_, 1.0 - opacity ) ); }
if( opacity > 0 && !endPixmap_.isNull() )
{ painter.drawPixmap( QPoint(0,0), fade( endPixmap_, opacity ) ); }
painter.end();
return;
}
//_________________________________________________________
QPixmap TitleAnimationData::BlendedPixmap::fade( QPixmap source, qreal opacity ) const
{
if( source.isNull() ) return QPixmap();
QPixmap out( source.size() );
out.fill( Qt::transparent );
// do nothing if opacity is too small
if( opacity*255 < 1 ) return out;
// draw pixmap
QPainter p( &out );
p.drawPixmap( QPoint(0,0), source );
// opacity mask
if( opacity*255 <= 254 )
{
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
QColor color( Qt::black );
color.setAlphaF( opacity );
p.fillRect(out.rect(), color );
}
p.end();
return out;
}
}

View file

@ -0,0 +1,226 @@
#ifndef oxygentitleanimationdata_h
#define oxygentitleanimationdata_h
//////////////////////////////////////////////////////////////////////////////
// oxygentitleanimationdata.h
// -------------------
//
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
// Copyright (c) 2003, 2004 David Johnson <david@usermode.org>
// Copyright (c) 2006, 2007 Riccardo Iaconelli <ruphy@fsfe.org>
//
// 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 "lib/oxygenanimation.h"
#include <cassert>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtGui/QPixmap>
namespace Oxygen
{
class TitleAnimationData: public QObject
{
Q_OBJECT
//! declare title opacity
Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity )
public:
typedef QPointer<TitleAnimationData> Pointer;
//! constructor
TitleAnimationData( QObject* );
//! reset
void reset( void )
{
setOpacity(0);
contrastPixmap_.reset();
pixmap_.reset();
}
//! initialize
void initialize( void );
// reset
void reset( QRect rect, QPixmap pixmap, QPixmap contrast )
{
setOpacity(0);
contrastPixmap_.reset( rect, contrast );
pixmap_.reset( rect, pixmap );
}
//! set pixmaps
void setPixmaps( QRect, QPixmap pixmap, QPixmap contrast );
//! duration
void setDuration( int duration )
{
assert( animation() );
animation().data()->setDuration( duration );
}
//! retrieve contrast pixmap
QPixmap contrastPixmap( void ) const
{ return contrastPixmap_.currentPixmap(); }
//! pixmap
QPixmap pixmap( void ) const
{ return pixmap_.currentPixmap(); }
//!@name animation
//@{
bool isAnimated( void ) const
{ return animation().data()->isRunning(); }
//! start animation
void startAnimation( void )
{
assert( !isAnimated() );
animation().data()->start();
}
//@}
//!@name opacity
//@{
qreal opacity( void ) const
{ return opacity_; }
void setOpacity( qreal value )
{ opacity_ = value; }
//@}
//! validity
bool isValid( void ) const
{ return pixmap_.isValid(); }
//! dirty flag
void setDirty( bool value )
{ dirty_ = value; }
//! dirty flag
bool isDirty( void ) const
{ return dirty_; }
signals:
virtual void pixmapsChanged( void );
protected slots:
//! update pixmaps
virtual void updatePixmaps( void );
protected:
//! animation object
const Animation::Pointer& animation( void ) const
{ return animation_; }
private:
//! used to blend pixmap
class BlendedPixmap
{
public:
// reset everything
void reset( void )
{
startRect_ = endRect_ = QRect();
startPixmap_ = endPixmap_ = currentPixmap_ = QPixmap();
}
//! reset
void reset( const QRect& rect, QPixmap pixmap )
{
startRect_ = endRect_ = rect;
startPixmap_ = endPixmap_ = currentPixmap_ = pixmap;
}
// update pixmaps
void initialize( const QRect& rect, QPixmap pixmap )
{
startRect_ = endRect_;
endRect_ = rect;
startPixmap_ = currentPixmap_;
endPixmap_ = pixmap;
}
//! update currentPixmap by blending start and end pixmap
void blend( qreal opacity );
//! current pixmap
QPixmap currentPixmap( void ) const
{ return currentPixmap_; }
//! validity
bool isValid( void ) const
{ return !(endRect_.isNull() || endPixmap_.isNull() ); }
protected:
// fade pixmap by some amount
QPixmap fade( QPixmap, qreal ) const;
private:
//! animation starting pixmap
QPixmap startPixmap_;
//! animation ending pixmap
QPixmap endPixmap_;
//! animation current pixmap
QPixmap currentPixmap_;
QRect startRect_;
QRect endRect_;
};
bool dirty_;
BlendedPixmap contrastPixmap_;
BlendedPixmap pixmap_;
//! title animation
Animation::Pointer animation_;
//! title opacity
qreal opacity_;
};
}
#endif