kwin/effects/coverswitch/coverswitch.cpp
Lucas Murray 1e91a66fd2 Added support for unstyled and frameless EffectFrames. Converted all
effects that display text or boxes to use the class. Minor speed
increase in XRender mode for those effects and a massive increase in
OpenGL mode (Double framerate in present windows in my case).

svn path=/trunk/KDE/kdebase/workspace/; revision=929951
2009-02-22 13:04:16 +00:00

1465 lines
56 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "coverswitch.h"
#include <kwinconfig.h>
#include <QFont>
#include <klocale.h>
#include <kapplication.h>
#include <kcolorscheme.h>
#include <kconfiggroup.h>
#include <kwinglutils.h>
#include <math.h>
#include <kdebug.h>
#include <GL/gl.h>
namespace KWin
{
KWIN_EFFECT( coverswitch, CoverSwitchEffect )
KWIN_EFFECT_SUPPORTED( coverswitch, CoverSwitchEffect::supported() )
CoverSwitchEffect::CoverSwitchEffect()
: mActivated( 0 )
, angle( 60.0 )
, animation( false )
, start( false )
, stop( false )
, stopRequested( false )
, startRequested( false )
, zPosition( 900.0 )
, scaleFactor( 0.0 )
, direction( Left )
, selected_window( 0 )
, captionFrame( EffectFrame::Styled )
, thumbnailFrame( EffectFrame::Styled )
{
reconfigure( ReconfigureAll );
// Caption frame
captionFont.setBold( true );
captionFont.setPointSize( captionFont.pointSize() * 2 );
captionFrame.setFont( captionFont );
}
CoverSwitchEffect::~CoverSwitchEffect()
{
}
bool CoverSwitchEffect::supported()
{
return effects->compositingType() == OpenGLCompositing;
}
void CoverSwitchEffect::reconfigure( ReconfigureFlags )
{
KConfigGroup conf = effects->effectConfig( "CoverSwitch" );
animationDuration = animationTime( conf, "Duration", 200 );
animateSwitch = conf.readEntry( "AnimateSwitch", true );
animateStart = conf.readEntry( "AnimateStart", true );
animateStop = conf.readEntry( "AnimateStop", true );
reflection = conf.readEntry( "Reflection", true );
zPosition = conf.readEntry( "ZPosition", 900.0 );
thumbnails = conf.readEntry( "Thumbnails", true );
dynamicThumbnails = conf.readEntry( "DynamicThumbnails", true );
thumbnailWindows = conf.readEntry( "ThumbnailWindows", 8 );
timeLine.setCurveShape( TimeLine::EaseInOutCurve );
timeLine.setDuration( animationDuration );
// thumbnail bar
color_frame = KColorScheme( QPalette::Active, KColorScheme::Window ).background().color();
color_frame.setAlphaF( 0.9 );
color_highlight = KColorScheme( QPalette::Active, KColorScheme::Selection ).background().color();
color_highlight.setAlphaF( 0.9 );
highlight_margin = 10;
}
void CoverSwitchEffect::prePaintScreen( ScreenPrePaintData& data, int time )
{
if( mActivated || stop || stopRequested )
{
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
if( animation || start || stop )
{
timeLine.addTime( (double)time );
if( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
calculateItemSizes();
}
if( effects->currentTabBoxWindow() == NULL )
abort();
}
effects->prePaintScreen(data, time);
}
void CoverSwitchEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
{
effects->paintScreen( mask, region, data );
if( mActivated || stop || stopRequested )
{
if( effects->numScreens() > 1 )
{
// unfortunatelly we have to change the projection matrix in dual screen mode
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
float fovy = 60.0f;
float aspect = 1.0f;
float zNear = 0.1f;
float zFar = 100.0f;
float ymax = zNear * tan( fovy * M_PI / 360.0f );
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
float xTranslate = 0.0;
float yTranslate = 0.0;
float xminFactor = 1.0;
float xmaxFactor = 1.0;
float yminFactor = 1.0;
float ymaxFactor = 1.0;
if( area.x() == 0 && area.width() != fullRect.width() )
{
// horizontal layout: left screen
xminFactor = (float)area.width()/(float)fullRect.width();
xmaxFactor = ((float)fullRect.width()-(float)area.width()*0.5f)/((float)fullRect.width()*0.5f);
xTranslate = (float)fullRect.width()*0.5f-(float)area.width()*0.5f;
}
if( area.x() != 0 && area.width() != fullRect.width() )
{
// horizontal layout: right screen
xminFactor = ((float)fullRect.width()-(float)area.width()*0.5f)/((float)fullRect.width()*0.5f);
xmaxFactor = (float)area.width()/(float)fullRect.width();
xTranslate = (float)fullRect.width()*0.5f-(float)area.width()*0.5f;
}
if( area.y() == 0 && area.height() != fullRect.height() )
{
// vertical layout: top screen
yminFactor = ((float)fullRect.height()-(float)area.height()*0.5f)/((float)fullRect.height()*0.5f);
ymaxFactor = (float)area.height()/(float)fullRect.height();
yTranslate = (float)fullRect.height()*0.5f-(float)area.height()*0.5f;
}
if( area.y() != 0 && area.height() != fullRect.height() )
{
// vertical layout: bottom screen
yminFactor = (float)area.height()/(float)fullRect.height();
ymaxFactor = ((float)fullRect.height()-(float)area.height()*0.5f)/((float)fullRect.height()*0.5f);
yTranslate = (float)fullRect.height()*0.5f-(float)area.height()*0.5f;
}
glFrustum( xmin*xminFactor, xmax*xmaxFactor, ymin*yminFactor, ymax*ymaxFactor, zNear, zFar );
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glTranslatef( xTranslate, yTranslate, 0.0 );
}
QList< EffectWindow* > tempList = effects->currentTabBoxWindowList();
int index = tempList.indexOf( effects->currentTabBoxWindow() );
if( animation || start || stop )
{
if( !start && !stop )
{
if( direction == Right )
index++;
else
index--;
if( index < 0 )
index = tempList.count() + index;
if( index >= tempList.count() )
index = index % tempList.count();
}
foreach( Direction direction, scheduled_directions )
{
if( direction == Right )
index++;
else
index--;
if( index < 0 )
index = tempList.count() + index;
if( index >= tempList.count() )
index = index % tempList.count();
}
}
int leftIndex = index -1;
if( leftIndex < 0 )
leftIndex = tempList.count() -1;
int rightIndex = index +1;
if( rightIndex == tempList.count() )
rightIndex = 0;
EffectWindow* frontWindow = tempList[ index ];
leftWindows.clear();
rightWindows.clear();
bool evenWindows = ( tempList.count() % 2 == 0 ) ? true : false;
int leftWindowCount = 0;
if( evenWindows )
leftWindowCount = tempList.count()/2 - 1;
else
leftWindowCount = ( tempList.count() - 1 )/2;
for( int i=0; i < leftWindowCount; i++ )
{
int tempIndex = ( leftIndex - i );
if( tempIndex < 0 )
tempIndex = tempList.count() + tempIndex;
leftWindows.prepend( tempList[ tempIndex ] );
}
int rightWindowCount = 0;
if( evenWindows )
rightWindowCount = tempList.count()/2;
else
rightWindowCount = ( tempList.count() - 1 )/2;
for( int i=0; i < rightWindowCount; i++ )
{
int tempIndex = ( rightIndex + i ) % tempList.count();
rightWindows.prepend( tempList[ tempIndex ] );
}
if( reflection )
{
// restrict painting the reflections to the current screen
QRegion clip = QRegion( area );
PaintClipper::push( clip );
// no reflections during start and stop animation
if( !start && !stop )
paintScene( frontWindow, &leftWindows, &rightWindows, true );
PaintClipper::pop( clip );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode( GL_FRONT, GL_FILL );
glPushMatrix();
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
// we can use a huge scale factor (needed to calculate the rearground vertices)
// as we restrict with a PaintClipper painting on the current screen
float reflectionScaleFactor = 100000 * tan( 60.0 * M_PI / 360.0f )/area.width();
if( effects->numScreens() > 1 && area.x() != fullRect.x() )
{
// have to change the reflection area in horizontal layout and right screen
glTranslatef( -area.x(), 0.0, 0.0 );
}
glTranslatef( area.x() + area.width()*0.5f, 0.0, 0.0 );
float vertices[] = {
-area.width()*0.5f, area.height(), 0.0,
area.width()*0.5f, area.height(), 0.0,
(float)area.width()*reflectionScaleFactor, area.height(), -5000,
-(float)area.width()*reflectionScaleFactor, area.height(), -5000 };
// foreground
float alpha = 1.0;
if( start )
alpha = timeLine.value();
else if( stop )
alpha = 1.0 - timeLine.value();
glColor4f( 0.0, 0.0, 0.0, alpha );
int y = 0;
// have to adjust the y values to fit OpenGL
// in OpenGL y==0 is at bottom, in Qt at top
if( effects->numScreens() > 1 )
{
QRect fullArea = effects->clientArea( FullArea, 0, 1 );
if( fullArea.height() != area.height() )
{
if( area.y() == 0 )
y = fullArea.height() - area.height();
else
y = fullArea.height() - area.y() - area.height();
}
}
// use scissor to restrict painting of the reflection plane to current screen
glScissor( area.x(), y, area.width(), area.height() );
glEnable( GL_SCISSOR_TEST );
glBegin( GL_POLYGON );
glVertex3f( vertices[0], vertices[1], vertices[2] );
glVertex3f( vertices[3], vertices[4], vertices[5] );
// rearground
alpha = -1.0;
glColor4f( 0.0, 0.0, 0.0, alpha );
glVertex3f( vertices[6], vertices[7], vertices[8] );
glVertex3f( vertices[9], vertices[10], vertices[11] );
glEnd();
glDisable( GL_SCISSOR_TEST );
glPopMatrix();
glDisable( GL_BLEND );
}
paintScene( frontWindow, &leftWindows, &rightWindows );
if( effects->numScreens() > 1 )
{
glPopMatrix();
// revert change of projection matrix
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
}
// Render the caption frame
double opacity = 1.0;
if( start )
opacity = timeLine.value();
else if( stop )
opacity = 1.0 - timeLine.value();
QPixmap iconPixmap = effects->currentTabBoxWindow()->icon();
if( start || stop )
{
int alpha = 255.0f * timeLine.value();
if( stop )
alpha = 255.0f - alpha;
QPixmap transparency = iconPixmap.copy( iconPixmap.rect() );
transparency.fill( QColor( 255, 255, 255, alpha ));
iconPixmap.setAlphaChannel( transparency.alphaChannel() );
}
captionFrame.setText( effects->currentTabBoxWindow()->caption() );
captionFrame.setIcon( iconPixmap );
captionFrame.render( region, opacity );
if( ( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
&& !( start || stop ) )
{
thumbnailFrame.render( region );
// HACK: PaintClipper is used because window split is somehow wrong if the height is greater than width
PaintClipper::push( frame_area );
paintHighlight( highlight_area );
foreach( EffectWindow* w, windows.keys())
{
paintWindowThumbnail( w );
paintWindowIcon( w );
}
PaintClipper::pop( frame_area );
}
}
}
void CoverSwitchEffect::postPaintScreen()
{
if( ( mActivated && ( animation || start ) ) || stop || stopRequested )
{
if( timeLine.value() == 1.0 )
{
timeLine.setProgress(0.0);
if( stop )
{
stop = false;
effects->setActiveFullScreenEffect( 0 );
if( startRequested )
{
startRequested = false;
mActivated = true;
effects->refTabBox();
if( animateStart )
{
start = true;
}
}
}
else if( !scheduled_directions.isEmpty() )
{
direction = scheduled_directions.dequeue();
if( start )
{
animation = true;
start = false;
}
}
else
{
animation = false;
start = false;
if( stopRequested )
{
stopRequested = false;
stop = true;
}
}
}
effects->addRepaintFull();
}
effects->postPaintScreen();
}
void CoverSwitchEffect::paintScene( EffectWindow* frontWindow, QList< EffectWindow* >* leftWindows,
QList< EffectWindow* >* rightWindows, bool reflectedWindows )
{
// LAYOUT
// one window in the front. Other windows left and right rotated
// for odd number of windows: left: (n-1)/2; front: 1; right: (n-1)/2
// for even number of windows: left: n/2; front: 1; right: n/2 -1
//
// ANIMATION
// forward (alt+tab)
// all left windows are moved to next position
// top most left window is rotated and moved to front window position
// front window is rotated and moved to next right window position
// right windows are moved to next position
// last right window becomes totally transparent in half the time
// appears transparent on left side and becomes totally opaque again
// backward (alt+shift+tab) same as forward but opposite direction
int width = area.width();
int leftWindowCount = leftWindows->count();
int rightWindowCount = rightWindows->count();
RotationData rot;
rot.axis = RotationData::YAxis;
// Problem during animation: a window which is painted after another window
// appears in front of the other
// so during animation the painting order has to be rearreanged
// paint sequence no animation: left, right, front
// paint sequence forward animation: right, front, left
if( !animation )
{
paintWindows( leftWindows, true, reflectedWindows );
paintWindows( rightWindows, false, reflectedWindows );
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
}
else
{
if( direction == Right )
{
if( timeLine.value() < 0.5 )
{
// paint in normal way
paintWindows( leftWindows, true, reflectedWindows );
paintWindows( rightWindows, false, reflectedWindows );
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
}
else
{
paintWindows( rightWindows, false, reflectedWindows );
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
paintWindows( leftWindows, true, reflectedWindows, rightWindows->at( 0 ) );
}
}
else
{
paintWindows( leftWindows, true, reflectedWindows );
if( timeLine.value() < 0.5 )
{
paintWindows( rightWindows, false, reflectedWindows );
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
}
else
{
EffectWindow* leftWindow;
if( leftWindowCount > 0)
{
leftWindow = leftWindows->at( 0 );
paintFrontWindow( frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows );
}
else
leftWindow = frontWindow;
paintWindows( rightWindows, false, reflectedWindows, leftWindow );
}
}
}
}
void CoverSwitchEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
if( mActivated || stop || stopRequested )
{
if( !( mask & PAINT_WINDOW_TRANSFORMED ) && !w->isDesktop() )
{
if( ( start || stop ) && w->isDock() )
{
data.opacity = 1.0 - timeLine.value();
if( stop )
data.opacity = timeLine.value();
}
else
return;
}
}
if ( ( start || stop ) && (!w->isOnCurrentDesktop() || w->isMinimized() ) )
{
if (stop) // Fade out windows not on the current desktop
data.opacity = (1.0 - timeLine.value());
else // Fade in Windows from other desktops when animation is started
data.opacity = timeLine.value();
}
effects->paintWindow( w, mask, region, data );
}
void CoverSwitchEffect::tabBoxAdded( int mode )
{
if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
return;
if( !mActivated )
{
// only for windows mode
if( mode == TabBoxWindowsMode && effects->currentTabBoxWindowList().count() > 0 )
{
input = effects->createFullScreenInputWindow( this, Qt::ArrowCursor );
activeScreen = effects->activeScreen();
if( !stop && !stopRequested )
{
effects->refTabBox();
effects->setActiveFullScreenEffect( this );
scheduled_directions.clear();
selected_window = effects->currentTabBoxWindow();
direction = Left;
mActivated = true;
if( animateStart )
{
start = true;
}
// Calculation of correct area
area = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop());
scaleFactor = (zPosition+1100) * 2.0 * tan( 60.0 * M_PI / 360.0f )/displayWidth();
if( displayWidth()-area.width() != 0 )
{
// one of the screens is smaller than the other (horizontal)
if( area.width() < displayWidth() - area.width() )
scaleFactor *= (float)area.width()/(float)(displayWidth()-area.width());
else if( area.width() != displayWidth() - area.width() )
{
// vertical layout with different width
// but we don't want to catch screens with same width and different height
if( displayHeight() != area.height() )
scaleFactor *= (float)area.width()/(float)(displayWidth());
}
}
// Setup caption frame geometry
QRect frameRect = QRect( area.width() * 0.25f + area.x(),
area.height() * 0.9f + area.y(),
area.width() * 0.5f,
QFontMetrics( captionFont ).height() );
captionFrame.setGeometry( frameRect );
captionFrame.setIconSize( QSize( frameRect.height(), frameRect.height() ));
effects->addRepaintFull();
}
else
{
startRequested = true;
}
if( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
{
highlight_is_set = false;
calculateFrameSize();
calculateItemSizes();
}
}
}
}
void CoverSwitchEffect::tabBoxClosed()
{
if( mActivated )
{
if( animateStop )
{
if( !animation && !start )
{
stop = true;
}
else if( start && scheduled_directions.isEmpty() )
{
start = false;
stop = true;
timeLine.setProgress( 1.0 - timeLine.value() );
}
else
{
stopRequested = true;
}
}
else
effects->setActiveFullScreenEffect( 0 );
mActivated = false;
effects->unrefTabBox();
effects->destroyInputWindow( input );
effects->addRepaintFull();
if( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
{
qDeleteAll( windows );
windows.clear();
}
}
}
void CoverSwitchEffect::tabBoxUpdated()
{
if( mActivated )
{
if( animateSwitch && effects->currentTabBoxWindowList().count() > 1)
{
// determine the switch direction
if( selected_window != effects->currentTabBoxWindow() )
{
if( selected_window != NULL )
{
int old_index = effects->currentTabBoxWindowList().indexOf( selected_window );
int new_index = effects->currentTabBoxWindowList().indexOf( effects->currentTabBoxWindow() );
Direction new_direction;
int distance = new_index - old_index;
if( distance > 0 )
new_direction = Left;
if( distance < 0 )
new_direction = Right;
if( effects->currentTabBoxWindowList().count() == 2 )
{
new_direction = Left;
distance = 1;
}
if( distance != 0 )
{
distance = abs( distance );
int tempDistance = effects->currentTabBoxWindowList().count() - distance;
if( tempDistance < abs( distance ) )
{
distance = tempDistance;
if( new_direction == Left )
new_direction = Right;
else
new_direction = Left;
}
if( !animation && !start )
{
animation = true;
direction = new_direction;
distance--;
}
for( int i=0; i<distance; i++ )
{
if( !scheduled_directions.isEmpty() && scheduled_directions.last() != new_direction )
scheduled_directions.pop_back();
else
scheduled_directions.enqueue( new_direction );
if( scheduled_directions.count() == effects->currentTabBoxWindowList().count() )
scheduled_directions.clear();
}
}
}
selected_window = effects->currentTabBoxWindow();
}
}
if( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
{
calculateFrameSize();
calculateItemSizes();
}
effects->addRepaintFull();
}
}
void CoverSwitchEffect::paintWindowCover( EffectWindow* w, bool reflectedWindow, WindowPaintData& data )
{
QRect windowRect = w->geometry();
data.yTranslate = area.height() - windowRect.y() - windowRect.height();
data.zTranslate = -zPosition;
if( start )
{
if( w->isMinimized() )
{
data.opacity *= timeLine.value();
}
else
{
data.xTranslate *= timeLine.value();
data.yTranslate *= timeLine.value();
if( effects->numScreens() > 1)
{
QRect clientRect = effects->clientArea( FullScreenArea, w->screen(), effects->currentDesktop() );
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
if( w->screen() == activeScreen )
{
if( clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x() )
{
data.xTranslate -= clientRect.x()*(1.0f-timeLine.value());
}
if( clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y() )
{
data.yTranslate -= clientRect.y()*(1.0f-timeLine.value());
}
}
else
{
if( clientRect.width() != fullRect.width() && clientRect.x() < area.x())
{
data.xTranslate -= clientRect.width()*(1.0f-timeLine.value());
}
if( clientRect.height() != fullRect.height() && clientRect.y() < area.y() )
{
data.yTranslate -= clientRect.height()*(1.0f-timeLine.value());
}
}
}
data.zTranslate *= timeLine.value();
if( data.rotation )
data.rotation->angle *= timeLine.value();
}
}
if( stop )
{
if( w->isMinimized() && w != effects->activeWindow() )
{
data.opacity *= (1.0 - timeLine.value());
}
else
{
data.xTranslate *= (1.0 - timeLine.value());
data.yTranslate *= (1.0 - timeLine.value());
if( effects->numScreens() > 1)
{
QRect clientRect = effects->clientArea( FullScreenArea, w->screen(), effects->currentDesktop() );
QRect rect = effects->clientArea( FullScreenArea, activeScreen, effects->currentDesktop() );
QRect fullRect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
if( w->screen() == activeScreen )
{
if( clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x() )
{
data.xTranslate -= clientRect.x()*timeLine.value();
}
if( clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y() )
{
data.yTranslate -= clientRect.y()*timeLine.value();
}
}
else
{
if( clientRect.width() != fullRect.width() && clientRect.x() < rect.x())
{
data.xTranslate -= clientRect.width()*timeLine.value();
}
if( clientRect.height() != fullRect.height() && clientRect.y() < area.y() )
{
data.yTranslate -= clientRect.height()*timeLine.value();
}
}
}
data.zTranslate *= (1.0 - timeLine.value());
if( data.rotation )
data.rotation->angle *= (1.0 - timeLine.value());
}
}
QRect thumbnail = infiniteRegion();
if( reflectedWindow )
{
glPushMatrix();
glScalef( 1.0, -1.0, 1.0 );
data.yTranslate = - area.height() - windowRect.y() - windowRect.height();
effects->paintWindow( w,
PAINT_WINDOW_TRANSFORMED,
infiniteRegion(), data );
glPopMatrix();
}
else
{
effects->paintWindow( w,
PAINT_WINDOW_TRANSFORMED,
infiniteRegion(), data );
}
}
void CoverSwitchEffect::paintFrontWindow( EffectWindow* frontWindow, int width, int leftWindows, int rightWindows, bool reflectedWindow )
{
if( frontWindow == NULL )
return;
float distance = 0.0;
bool specialHandlingForward = false;
WindowPaintData data( frontWindow );
data.xTranslate = area.width()*0.5 - frontWindow->geometry().x() - frontWindow->geometry().width()*0.5;
if( leftWindows == 0 )
{
leftWindows = 1;
if( !start && !stop )
specialHandlingForward = true;
}
if( rightWindows == 0 )
{
rightWindows = 1;
}
if( animation )
{
if( direction == Right )
{
// move to right
distance = -frontWindow->geometry().width()*0.5f + area.width()*0.5f +
(((float)displayWidth()*0.5*scaleFactor)-(float)area.width()*0.5f)/rightWindows;
data.xTranslate += distance * timeLine.value();
RotationData rot;
rot.axis = RotationData::YAxis;
rot.angle = -angle*timeLine.value();
rot.xRotationPoint = frontWindow->geometry().width();
data.rotation = &rot;
}
else
{
// move to left
distance = frontWindow->geometry().width()*0.5f - area.width()*0.5f +
((float)width*0.5f-((float)displayWidth()*0.5*scaleFactor))/leftWindows;
float factor = 1.0;
if( specialHandlingForward )
factor = 2.0;
data.xTranslate += distance * timeLine.value() * factor;
RotationData rot;
rot.axis = RotationData::YAxis;
rot.angle = angle*timeLine.value();
data.rotation = &rot;
}
}
if( specialHandlingForward )
{
data.opacity *= (1.0 - timeLine.value() * 2.0);
paintWindowCover( frontWindow, reflectedWindow, data );
}
else
paintWindowCover( frontWindow, reflectedWindow, data );
}
void CoverSwitchEffect::paintWindows( QList< EffectWindow* >* windows, bool left, bool reflectedWindows, EffectWindow* additionalWindow )
{
int width = area.width();
int windowCount = windows->count();
EffectWindow* window;
int rotateFactor = 1;
if( !left )
{
rotateFactor = -1;
}
float xTranslate = -((float)(width)*0.5f-((float)displayWidth()*0.5*scaleFactor));
if( !left )
xTranslate = ((float)displayWidth()*0.5*scaleFactor)-(float)width*0.5f;
// handling for additional window from other side
// has to appear on this side after half of the time
if( animation && timeLine.value() >= 0.5 && additionalWindow != NULL )
{
RotationData rot;
rot.axis = RotationData::YAxis;
rot.angle = angle;
rot.angle = angle*rotateFactor;
WindowPaintData data( additionalWindow );
if( left )
data.xTranslate += -xTranslate - additionalWindow->geometry().x();
else
{
data.xTranslate += xTranslate + area.width() -
additionalWindow->geometry().x() - additionalWindow->geometry().width();
rot.xRotationPoint = additionalWindow->geometry().width();
}
data.rotation = &rot;
data.opacity *= ( timeLine.value() - 0.5 ) * 2.0;
paintWindowCover( additionalWindow, reflectedWindows, data );
}
RotationData rot;
rot.axis = RotationData::YAxis;
// normal behaviour
for( int i=0; i < windowCount; i++ )
{
window = windows->at( i );
if( window == NULL )
continue;
WindowPaintData data( window );
rot.angle = angle;
if( left )
data.xTranslate += -xTranslate + xTranslate*i/windowCount - window->geometry().x();
else
data.xTranslate += xTranslate + width - xTranslate*i/windowCount - window->geometry().x() - window->geometry().width();
if( animation )
{
if( direction == Right )
{
if( ( i == windowCount - 1 ) && left )
{
// right most window on left side -> move to front
// have to move one window distance plus half the difference between the window and the desktop size
data.xTranslate += (xTranslate/windowCount + (width - window->geometry().width())*0.5f)*timeLine.value();
rot.angle = ( angle - angle * timeLine.value() );
}
// right most window does not have to be moved
else if( !left && ( i == 0 ) ); // do nothing
else
{
// all other windows - move to next position
data.xTranslate += xTranslate/windowCount * timeLine.value();
}
}
else
{
if( ( i == windowCount - 1 ) && !left )
{
// left most window on right side -> move to front
data.xTranslate -= (xTranslate/windowCount + (width - window->geometry().width())*0.5f)*timeLine.value();
rot.angle = ( angle - angle * timeLine.value() );
}
// left most window does not have to be moved
else if( i==0 && left); // do nothing
else
{
// all other windows - move to next position
data.xTranslate -= xTranslate/windowCount * timeLine.value();
}
}
}
if( left )
rot.xRotationPoint = 0.0;
else
rot.xRotationPoint = window->geometry().width();
rot.angle *= rotateFactor;
data.rotation = &rot;
// make window most to edge transparent if animation
if( animation && i == 0 && ( ( direction == Left && left ) || ( direction == Right && !left ) ) )
{
// only for the first half of the animation
if( timeLine.value() < 0.5 )
{
data.opacity *= (1.0 - timeLine.value() * 2.0);
paintWindowCover( window, reflectedWindows, data );
}
}
else
{
paintWindowCover( window, reflectedWindows, data );
}
}
}
// thumbnail bar - taken from boxswitch effect
void CoverSwitchEffect::calculateFrameSize()
{
int itemcount;
QRect screenr = effects->clientArea( PlacementArea, activeScreen, effects->currentDesktop());
itemcount = effects->currentTabBoxWindowList().count();
item_max_size.setWidth( (screenr.width()*0.95f * 2)/itemcount );
if( item_max_size.width() > 250 )
item_max_size.setWidth( 250 );
item_max_size.setHeight( item_max_size.width() * ((float)screenr.height()/(float)screenr.width()) );
// Shrink the size until all windows/desktops can fit onscreen
frame_area.setWidth( itemcount * item_max_size.width());
frame_area.setHeight( item_max_size.height() );
frame_area.moveTo( screenr.x() + ( screenr.width() - frame_area.width()) / 2,
screenr.y() + screenr.height()*0.05f );
thumbnailFrame.setGeometry( frame_area );
}
void CoverSwitchEffect::calculateItemSizes()
{
windows.clear();
EffectWindowList original_windows = effects->currentTabBoxWindowList();
int index = original_windows.indexOf( effects->currentTabBoxWindow() );
int leftIndex = index;
int rightIndex = index + 1;
if( rightIndex == original_windows.count() )
rightIndex = 0;
EffectWindowList ordered_windows;
int leftWindowCount = original_windows.count()/2;
int rightWindowCount = leftWindowCount;
if( original_windows.count()%2 == 1 )
leftWindowCount++;
for( int i=0; i < leftWindowCount; i++ )
{
int tempIndex = ( leftIndex - i );
if( tempIndex < 0 )
tempIndex = original_windows.count() + tempIndex;
ordered_windows.prepend( original_windows[ tempIndex ] );
}
for( int i=0; i < rightWindowCount; i++ )
{
int tempIndex = ( rightIndex + i ) % original_windows.count();
ordered_windows.append( original_windows[ tempIndex ] );
}
// move items cause of schedule
for( int i=0; i < scheduled_directions.count(); i++ )
{
Direction actual = scheduled_directions[ i ];
if( actual == Left )
{
EffectWindow* w = ordered_windows.takeLast();
ordered_windows.prepend( w );
}
else
{
EffectWindow* w = ordered_windows.takeFirst();
ordered_windows.append( w );
}
}
if( animation && timeLine.value() < 0.5 )
{
if( direction == Left )
{
EffectWindow* w = ordered_windows.takeLast();
edge_window = w;
ordered_windows.prepend( w );
}
else
{
EffectWindow* w = ordered_windows.takeFirst();
edge_window = w;
ordered_windows.append( w );
}
}
else if( animation && timeLine.value() >= 0.5 )
{
if( animation && direction == Left )
edge_window = ordered_windows.last();
if( animation && direction == Right )
edge_window = ordered_windows.first();
}
int offset = 0;
if( animation )
{
if( direction == Left )
offset = (float)item_max_size.width()*(1.0-timeLine.value());
else
offset = -(float)item_max_size.width()*(1.0-timeLine.value());
}
for( int i = 0; i < ordered_windows.count(); i++ )
{
EffectWindow* w = ordered_windows.at( i );
windows[ w ] = new ItemInfo();
float moveIndex = i;
if( animation && timeLine.value() < 0.5 )
{
if( direction == Left )
moveIndex--;
else
moveIndex++;
}
if( ordered_windows.count()%2 == 0 )
moveIndex += 0.5;
windows[ w ]->area = QRect( frame_area.x() + moveIndex * item_max_size.width() + offset,
frame_area.y(),
item_max_size.width(), item_max_size.height());
windows[ w ]->clickable = windows[ w ]->area;
}
if( ordered_windows.count()%2 == 0 )
{
right_window = ordered_windows.last();
}
if( !highlight_is_set )
{
highlight_area = windows[ effects->currentTabBoxWindow() ]->area;
highlight_is_set = true;
}
}
void CoverSwitchEffect::paintHighlight( QRect area )
{
glPushAttrib( GL_CURRENT_BIT );
glColor4f( color_highlight.redF(), color_highlight.greenF(), color_highlight.blueF(), color_highlight.alphaF());
renderRoundBox( area, 6 );
glPopAttrib();
}
void CoverSwitchEffect::paintWindowThumbnail( EffectWindow* w )
{
if( !windows.contains( w ))
return;
WindowPaintData data( w );
setPositionTransformations( data,
windows[ w ]->thumbnail, w,
windows[ w ]->area.adjusted( highlight_margin, highlight_margin, -highlight_margin, -highlight_margin ),
Qt::KeepAspectRatio );
if( animation && ( w == edge_window ) && ( windows.size() % 2 == 1 ) )
{
// in case of animation:
// the window which has to change the side will be split and painted on both sides of the edge
double splitPoint = 0.0;
if( direction == Left )
{
splitPoint = w->geometry().width()*timeLine.value();
}
else
{
splitPoint = w->geometry().width() - w->geometry().width()*timeLine.value();
}
data.quads = data.quads.splitAtX( splitPoint );
WindowQuadList left_quads;
WindowQuadList right_quads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.left() >= splitPoint )
left_quads << quad;
if( quad.right() <= splitPoint )
right_quads << quad;
}
// the base position of the window is changed after half of the animation
if( timeLine.value() < 0.5 )
{
if( direction == Left )
data.quads = left_quads;
else
data.quads = right_quads;
}
else
{
if( direction == Left )
data.quads = right_quads;
else
data.quads = left_quads;
}
// paint one part of the thumbnail
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
QRect secondThumbnail;
// paint the second part of the thumbnail:
// the other window quads are needed and a new rect for transformation has to be calculated
if( direction == Left )
{
if( timeLine.value() < 0.5 )
{
data.quads = right_quads;
secondThumbnail = QRect( frame_area.x() + frame_area.width() -
(float)item_max_size.width()*timeLine.value(),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
else
{
data.quads = left_quads;
secondThumbnail = QRect( frame_area.x() - (float)item_max_size.width()*timeLine.value(),
frame_area.y(), item_max_size.width(), item_max_size.height());
if( right_window )
secondThumbnail = QRect( frame_area.x() -
(float)item_max_size.width()*(timeLine.value()-0.5),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
}
else
{
if( timeLine.value() < 0.5 )
{
data.quads = left_quads;
secondThumbnail = QRect( frame_area.x() -
(float)item_max_size.width()*(1.0 - timeLine.value()),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
else
{
data.quads = right_quads;
secondThumbnail = QRect( frame_area.x() + frame_area.width() -
(float)item_max_size.width()*(1.0 - timeLine.value()),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
}
setPositionTransformations( data,
windows[ w ]->thumbnail, w,
secondThumbnail.adjusted( highlight_margin, highlight_margin, -highlight_margin, -highlight_margin ),
Qt::KeepAspectRatio );
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
}
else if( ( windows.size() % 2 == 0 ) && ( w == right_window ) )
{
// in case of even number of thumbnails:
// the window on the right is painted one half on left and on half on the right side
float animationOffset = 0.0f;
double splitPoint = w->geometry().width()*0.5;
if( animation && timeLine.value() <= 0.5 )
{
// in case of animation the right window has only to be split during first half of animation
if( direction == Left )
{
splitPoint += w->geometry().width()*timeLine.value();
animationOffset = -(float)item_max_size.width()*timeLine.value();
}
else
{
splitPoint -= w->geometry().width()*timeLine.value();
animationOffset = (float)item_max_size.width()*timeLine.value();
}
}
if( animation && timeLine.value() > 0.5 )
{
// at half of animation a different window has become the right window
// we have to adjust the splitting again
if( direction == Left )
{
splitPoint -= w->geometry().width()*(1.0-timeLine.value());
animationOffset = (float)item_max_size.width()*(1.0-timeLine.value());
}
else
{
splitPoint += w->geometry().width()*(1.0-timeLine.value());
animationOffset = -(float)item_max_size.width()*(1.0-timeLine.value());
}
}
data.quads = data.quads.splitAtX( splitPoint );
WindowQuadList rightQuads;
WindowQuadList leftQuads;
foreach( const WindowQuad &quad, data.quads )
{
if( quad.right() <= splitPoint )
leftQuads << quad;
else
rightQuads << quad;
}
// left quads are painted on right side of frame
data.quads = leftQuads;
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
// right quads are painted on left side of frame
data.quads = rightQuads;
QRect secondThumbnail;
secondThumbnail = QRect( frame_area.x() -
(float)item_max_size.width()*0.5 + animationOffset,
frame_area.y(), item_max_size.width(), item_max_size.height());
setPositionTransformations( data,
windows[ w ]->thumbnail, w,
secondThumbnail.adjusted( highlight_margin, highlight_margin, -highlight_margin, -highlight_margin ),
Qt::KeepAspectRatio );
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
}
else
{
effects->drawWindow( w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
windows[ w ]->thumbnail, data );
}
}
void CoverSwitchEffect::paintWindowIcon( EffectWindow* w )
{
if( !windows.contains( w ))
return;
// Don't render null icons
if( w->icon().isNull() )
{
return;
}
if( windows[ w ]->icon.cacheKey() != w->icon().cacheKey())
{ // make sure windows[ w ]->icon is the right QPixmap, and rebind
windows[ w ]->icon = w->icon();
if( ( windows.size() % 2 == 1 ) && animation && w == edge_window )
{
int alpha;
if( timeLine.value() < 0.5 )
alpha = 255.0f * (1.0 - timeLine.value()*2.0);
else
alpha = 255.0f * (timeLine.value()*2.0 - 1.0);
QPixmap transparency = windows[ w ]->icon.copy( windows[ w ]->icon.rect() );
transparency.fill( QColor( 255, 255, 255, alpha ) );
windows[ w ]->icon.setAlphaChannel( transparency.alphaChannel() );
}
windows[ w ]->iconTexture.load( windows[ w ]->icon );
windows[ w ]->iconTexture.setFilter( GL_LINEAR );
}
int width = windows[ w ]->icon.width();
int height = windows[ w ]->icon.height();
int x = windows[ w ]->area.x() + windows[ w ]->area.width() - width - highlight_margin;
int y = windows[ w ]->area.y() + windows[ w ]->area.height() - height - highlight_margin;
if( ( windows.size() % 2 == 0 ) )
{
if( w == right_window )
{
// in case of right window the icon has to be painted on the left side of the frame
x = frame_area.x() + windows[ w ]->area.width()*0.5 - width - highlight_margin;
if( animation )
{
if( timeLine.value() <= 0.5 )
{
if( direction == Left )
{
x -= windows[ w ]->area.width()*timeLine.value();
x = qMax( x, frame_area.x() );
}
else
x += windows[ w ]->area.width()*timeLine.value();
}
else
{
if( direction == Left )
x += windows[ w ]->area.width()*(1.0-timeLine.value());
else
{
x -= windows[ w ]->area.width()*(1.0-timeLine.value());
x = qMax( x, frame_area.x() );
}
}
}
}
}
else
{
// during animation the icon of the edge window has to change position
if( animation && w == edge_window )
{
if( timeLine.value() < 0.5 )
{
if( direction == Left )
x += windows[ w ]->area.width()*timeLine.value();
else
x -= windows[ w ]->area.width()*timeLine.value();
}
else
{
if( direction == Left )
x -= windows[ w ]->area.width()*(1.0 - timeLine.value());
else
x += windows[ w ]->area.width()*(1.0 - timeLine.value());
}
}
}
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Render some background
float alpha = 0.5;
if( ( windows.size() % 2 == 1 ) && animation && w == edge_window )
{
if( timeLine.value() < 0.5 )
alpha *= (1.0 - timeLine.value()*2.0);
else
alpha *= (timeLine.value()*2.0 - 1.0);
}
glColor4f( 0, 0, 0, alpha );
renderRoundBox( QRect( x-3, y-3, width+6, height+6 ), 3 );
// Render the icon
glColor4f( 1, 1, 1, 1 );
windows[ w ]->iconTexture.bind();
windows[ w ]->iconTexture.render( infiniteRegion(), QRect( x, y, width, height ));
windows[ w ]->iconTexture.unbind();
glPopAttrib();
}
void CoverSwitchEffect::windowInputMouseEvent( Window w, QEvent* e )
{
assert( w == input );
if( e->type() != QEvent::MouseButtonPress )
return;
// we don't want click events during animations
if( animation )
return;
QPoint pos = static_cast< QMouseEvent* >( e )->pos();
// has one of the thumbnails been clicked?
if( (thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows))))
{
// determine which item was clicked
foreach( EffectWindow* w, windows.keys())
{
if( windows[ w ]->clickable.contains( pos ))
{
effects->setTabBoxWindow( w );
return;
}
}
// special handling for second half of window in case of animation and even number of windows
if( windows.size() % 2 == 0 )
{
QRect additionalRect = QRect( frame_area.x(), frame_area.y(),
item_max_size.width()*0.5, item_max_size.height());
if( additionalRect.contains( pos ))
{
effects->setTabBoxWindow( right_window );
return;
}
}
}
// determine if a window has been clicked
// not interested in events above a fullscreen window (ignoring panel size)
if( pos.y() < (area.height()*scaleFactor - area.height())*0.5f*(1.0f/scaleFactor) )
return;
if( pos.x() < (area.width()*scaleFactor - selected_window->width())*0.5f*(1.0f/scaleFactor) )
{
float availableSize = (area.width()*scaleFactor - area.width())*0.5f*(1.0f/scaleFactor);
for( int i=0;i<leftWindows.count();i++ )
{
int windowPos = availableSize/leftWindows.count()*i;
if( pos.x() < windowPos )
continue;
if( i+1 < leftWindows.count() )
{
if( pos.x() > availableSize/leftWindows.count()*(i+1) )
continue;
}
effects->setTabBoxWindow( leftWindows[i] );
return;
}
}
if( pos.x() > area.width() - (area.width()*scaleFactor - selected_window->width())*0.5f*(1.0f/scaleFactor) )
{
float availableSize = (area.width()*scaleFactor - area.width())*0.5f*(1.0f/scaleFactor);
for( int i=0;i<rightWindows.count();i++ )
{
int windowPos = area.width() - availableSize/rightWindows.count()*i;
if( pos.x() > windowPos )
continue;
if( i+1 < rightWindows.count() )
{
if( pos.x() < area.width() - availableSize/rightWindows.count()*(i+1) )
continue;
}
effects->setTabBoxWindow( rightWindows[i] );
return;
}
}
}
void CoverSwitchEffect::abort()
{
effects->unrefTabBox();
effects->setActiveFullScreenEffect( 0 );
effects->destroyInputWindow( input );
mActivated = false;
stop = false;
stopRequested = false;
effects->addRepaintFull();
if( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
{
qDeleteAll( windows );
windows.clear();
}
captionFrame.free();
thumbnailFrame.free();
}
} // namespace