kwin/effects/coverswitch.cpp

1480 lines
57 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 )
{
reconfigure( ReconfigureAll );
}
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 );
frame_margin = 10;
highlight_margin = 5;
}
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();
}
}
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 );
}
// caption of selected window
QColor color_frame;
QColor color_text;
color_frame = KColorScheme( QPalette::Active, KColorScheme::Window ).background().color();
color_frame.setAlphaF( 0.9 );
color_text = KColorScheme( QPalette::Active, KColorScheme::Window ).foreground().color();
if( start )
{
color_frame.setAlphaF( 0.9 * timeLine.value() );
color_text.setAlphaF( timeLine.value() );
}
else if( stop )
{
color_frame.setAlphaF( 0.9 - 0.9 * timeLine.value() );
color_text.setAlphaF( 1.0 - timeLine.value() );
}
QFont text_font;
text_font.setBold( true );
text_font.setPointSize( 20 );
glPushAttrib( GL_CURRENT_BIT );
glColor4f( color_frame.redF(), color_frame.greenF(), color_frame.blueF(), color_frame.alphaF());
QRect frameRect = QRect( area.width()*0.25f + area.x(),
area.height()*0.9f + area.y(),
area.width()*0.5f,
QFontMetrics( text_font ).height() * 1.2f );
renderRoundBoxWithEdge( frameRect );
effects->paintText( effects->currentTabBoxWindow()->caption(),
frameRect.center(),
frameRect.width() - 100,
color_text,
text_font );
glPopAttrib();
// icon of selected window
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() );
}
GLTexture* icon = new GLTexture( iconPixmap );
icon->bind();
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
icon->bind();
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// icon takes 80 % of the height of the frame. So each 10 % space left on the top and bottom
QRect iconRect = QRect( frameRect.x() + frameRect.height()*0.1f,
frameRect.y() + frameRect.height()*0.1f,
frameRect.height()*0.8f,
frameRect.height()*0.8f );
icon->render( region, iconRect);
icon->unbind();
delete icon;
glDisable( GL_BLEND );
glPopAttrib();
if( ( thumbnails && (!dynamicThumbnails ||
(dynamicThumbnails && effects->currentTabBoxWindowList().size() >= thumbnailWindows)) )
&& !( start || stop ) )
{
paintFrame();
// HACK: PaintClipper is used because window split is somehow wrong if the height is greater than width
PaintClipper::push( frame_area.adjusted( frame_margin, frame_margin, -frame_margin, -frame_margin ));
paintHighlight( highlight_area );
foreach( EffectWindow* w, windows.keys())
{
paintWindowThumbnail( w );
paintWindowIcon( w );
}
PaintClipper::pop( frame_area.adjusted( frame_margin, frame_margin, -frame_margin, -frame_margin ) );
}
}
}
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());
}
}
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 - frame_margin * 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( frame_margin * 2 + itemcount * item_max_size.width());
frame_area.setHeight( frame_margin * 2 + item_max_size.height() );
frame_area.moveTo( screenr.x() + ( screenr.width() - frame_area.width()) / 2,
screenr.y() + screenr.height()*0.05f );
}
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() + frame_margin
+ moveIndex * item_max_size.width() + offset,
frame_area.y() + frame_margin,
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::paintFrame()
{
glPushAttrib( GL_CURRENT_BIT );
glColor4f( color_frame.redF(), color_frame.greenF(), color_frame.blueF(), color_frame.alphaF());
renderRoundBoxWithEdge( frame_area );
glPopAttrib();
}
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() - frame_margin
- (float)item_max_size.width()*timeLine.value(),
frame_area.y() + frame_margin, item_max_size.width(), item_max_size.height());
}
else
{
data.quads = left_quads;
secondThumbnail = QRect( frame_area.x() + frame_margin - (float)item_max_size.width()*timeLine.value(),
frame_area.y() + frame_margin, item_max_size.width(), item_max_size.height());
if( right_window )
secondThumbnail = QRect( frame_area.x() + frame_margin -
(float)item_max_size.width()*(timeLine.value()-0.5),
frame_area.y() + frame_margin, item_max_size.width(), item_max_size.height());
}
}
else
{
if( timeLine.value() < 0.5 )
{
data.quads = left_quads;
secondThumbnail = QRect( frame_area.x() + frame_margin -
(float)item_max_size.width()*(1.0 - timeLine.value()),
frame_area.y() + frame_margin, item_max_size.width(), item_max_size.height());
}
else
{
data.quads = right_quads;
secondThumbnail = QRect( frame_area.x() + frame_area.width() - frame_margin
- (float)item_max_size.width()*(1.0 - timeLine.value()),
frame_area.y() + frame_margin, 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() + frame_margin -
(float)item_max_size.width()*0.5 + animationOffset,
frame_area.y() + frame_margin, 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() + frame_margin );
}
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() + frame_margin );
}
}
}
}
}
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_margin,
frame_area.y() + frame_margin,
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;
}
}
}
} // namespace