/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin 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 . *********************************************************************/ #include "coverswitch.h" #include #include #include #include #include #include #include #include #include #include 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; icurrentTabBoxWindowList().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 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 windowPos ) continue; if( i+1 < rightWindows.count() ) { if( pos.x() < area.width() - availableSize/rightWindows.count()*(i+1) ) continue; } effects->setTabBoxWindow( rightWindows[i] ); return; } } } } // namespace