From fe4c100195b1d05637eca21b63cfef345c2747d4 Mon Sep 17 00:00:00 2001 From: Lucas Murray Date: Tue, 12 Aug 2008 15:22:06 +0000 Subject: [PATCH] Improved the desktop grid effect. FEATURE: 163104, 167265, 168557 CCBUG: 156247, 157196, 158787 svn path=/trunk/KDE/kdebase/workspace/; revision=845893 --- COMPOSITE_TODO | 1 - effects/CMakeLists.txt | 3 + effects/desktopgrid.cpp | 906 +++++++++++++++++---------------- effects/desktopgrid.desktop | 6 +- effects/desktopgrid.h | 57 ++- effects/desktopgrid_config.cpp | 188 ++++--- effects/desktopgrid_config.h | 21 +- effects/desktopgrid_config.ui | 178 +++++++ effects/slide.cpp | 266 ++++++++++ effects/slide.desktop | 17 + effects/slide.h | 56 ++ 11 files changed, 1148 insertions(+), 551 deletions(-) create mode 100644 effects/desktopgrid_config.ui create mode 100644 effects/slide.cpp create mode 100644 effects/slide.desktop create mode 100644 effects/slide.h diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 60336a717c..f3dededc20 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -229,7 +229,6 @@ Effects TODO / desktopgrid + - there's a bug that causes overlapping windows be shown also on desktops painted sooner than the window's desktop - needs clipping (and that probably needs support for cumulating clipping) -! - make desktop borders distinctive (i.e. paint a grid), to make it more visible that desktops are still separate Performance diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 7bcaf3a6c6..bf584ad1b8 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -51,6 +51,7 @@ SET(kwin4_effect_builtins_sources shadow.cpp showfps.cpp showpaint.cpp + slide.cpp taskbarthumbnail.cpp thumbnailaside.cpp zoom.cpp @@ -74,6 +75,7 @@ install( FILES shadow.desktop showfps.desktop showpaint.desktop + slide.desktop taskbarthumbnail.desktop thumbnailaside.desktop zoom.desktop @@ -82,6 +84,7 @@ install( FILES # config modules SET(kwin4_effect_builtins_config_sources desktopgrid_config.cpp + desktopgrid_config.ui diminactive_config.cpp diminactive_config.ui maketransparent_config.cpp diff --git a/effects/desktopgrid.cpp b/effects/desktopgrid.cpp index 7488aac4b2..dd9f69acbb 100644 --- a/effects/desktopgrid.cpp +++ b/effects/desktopgrid.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak +Copyright (C) 2008 Lucas Murray 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 @@ -37,22 +38,41 @@ KWIN_EFFECT( desktopgrid, DesktopGridEffect ) DesktopGridEffect::DesktopGridEffect() : activated( false ) - , keyboard_grab( false ) - , was_window_move( false ) - , window_move( NULL ) - , slide( false ) + , timeline() + , keyboardGrab( false ) + , wasWindowMove( false ) + , windowMove( NULL ) + , windowMoveDiff() + , gridSize() + , orientation( Qt::Horizontal ) + , activeCell( 1, 1 ) + , scale() + , unscaledBorder() + , scaledSize() + , scaledOffset() { + // Load shortcuts KActionCollection* actionCollection = new KActionCollection( this ); - KAction* a = static_cast< KAction* >( actionCollection->addAction( "ShowDesktopGrid" )); - a->setText( i18n("Show Desktop Grid" )); + KAction* a = (KAction*) actionCollection->addAction( "ShowDesktopGrid" ); + a->setText( i18n( "Show Desktop Grid" )); a->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F8 )); - connect( a, SIGNAL( triggered( bool )), this, SLOT( toggle())); - KConfigGroup conf = effects->effectConfig("DesktopGrid"); - slideEnabled = conf.readEntry( "Slide", true ); + connect( a, SIGNAL( triggered( bool )), this, SLOT( toggle() )); - borderActivate = (ElectricBorder)conf.readEntry("BorderActivate", (int)ElectricNone); + // Load all other configuration details + + KConfigGroup conf = effects->effectConfig( "DesktopGrid" ); + + borderActivate = ElectricBorder( conf.readEntry( "BorderActivate", int( ElectricNone ))); effects->reserveElectricBorder( borderActivate ); - mTimeLine.setCurveShape(TimeLine::EaseInOutCurve); + + zoomDuration = conf.readEntry( "ZoomDuration", 500 ); + timeline.setCurveShape( TimeLine::EaseInOutCurve ); + timeline.setDuration( zoomDuration ); + + border = conf.readEntry( "BorderWidth", 10 ); + desktopNameAlignment = Qt::Alignment( conf.readEntry( "DesktopNameAlignment", 0 )); + customLayout = conf.readEntry( "CustomLayout", 0 ) ? true : false; + customLayoutRows = conf.readEntry( "CustomLayoutRows", 2 ); } DesktopGridEffect::~DesktopGridEffect() @@ -60,467 +80,234 @@ DesktopGridEffect::~DesktopGridEffect() effects->unreserveElectricBorder( borderActivate ); } +//----------------------------------------------------------------------------- +// Screen painting + void DesktopGridEffect::prePaintScreen( ScreenPrePaintData& data, int time ) { - if( slide ) - { - mTimeLine.addTime(time); - - // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once, - // so with normal screen painting second screen paint would erase parts of the first paint - if( mTimeLine.value() != 1 ) - data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST; - else - { - slide = false; - mTimeLine.setProgress(0); - } - } - else if( mTimeLine.value() != 0 || activated ) + if( timeline.value() != 0 || activated ) { if( activated ) - mTimeLine.addTime(time); + timeline.addTime(time); else - mTimeLine.removeTime(time); + timeline.removeTime(time); + for( int i = 0; i < effects->numberOfDesktops(); i++ ) + { + if( i == highlightedDesktop - 1 ) + hoverTimeline[i].addTime(time); + else + hoverTimeline[i].removeTime(time); + } // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once, // so with normal screen painting second screen paint would erase parts of the first paint - if( mTimeLine.value() != 0 ) + if( timeline.value() != 0 ) data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST; - if( !activated && mTimeLine.value() == 0 ) + if( !activated && timeline.value() == 0 ) finish(); } effects->prePaintScreen( data, time ); } -void DesktopGridEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) - { - if( slide ) - { - if( w->isOnAllDesktops()) - { - if( slide_painting_sticky ) - data.setTransformed(); - else - w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - } - else if( w->isOnDesktop( painting_desktop )) - w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - else - w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - } - else if( mTimeLine.value() != 0 ) - { - if( w->isOnDesktop( painting_desktop )) - w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - else - w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - if( w == window_move ) - { - data.setTransformed(); - if( w->isOnAllDesktops() && painting_desktop != posToDesktop( window_move_pos - window_move_diff )) - w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); - } - } - effects->prePaintWindow( w, data, time ); - } - void DesktopGridEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) { - if( mTimeLine.value() == 0 ) + if( timeline.value() == 0 ) { effects->paintScreen( mask, region, data ); return; } - if( slide ) + for( int desktop = 1; desktop <= effects->numberOfDesktops(); desktop++ ) { - paintSlide( mask, region, data ); - return; - } - int desktop_with_move = -1; - if( window_move != NULL ) - desktop_with_move = window_move->isOnAllDesktops() - ? posToDesktop( window_move_pos - window_move_diff ) : window_move->desktop(); - for( int desktop = 1; - desktop <= effects->numberOfDesktops(); - ++desktop ) - { - if( desktop != desktop_with_move ) - { - PaintClipper pc( desktopRect( desktop, true )); - paintScreenDesktop( desktop, mask, region, data ); - } - } - // paint the desktop with the window being moved as the last one, i.e. on top of others - if( desktop_with_move != -1 ) - { - QRegion paintreg = desktopRect( desktop_with_move, true ); // paint only the desktop - paintreg |= windowRect( window_move ); // and wherever the moved window is - PaintClipper pc( paintreg ); - paintScreenDesktop( desktop_with_move, mask, region, data ); - } - } - -void DesktopGridEffect::paintScreenDesktop( int desktop, int mask, QRegion region, ScreenPaintData data ) - { - QRect rect = desktopRect( desktop, true ); - if( region.contains( rect )) // this desktop needs painting - { - painting_desktop = desktop; ScreenPaintData d = data; - QRect normal = desktopRect( effects->currentDesktop(), false ); - d.xTranslate += rect.x(); // - normal.x(); - d.yTranslate += rect.y(); // - normal.y(); - d.xScale *= rect.width() / double( normal.width()); - d.yScale *= rect.height() / double( normal.height()); - // TODO mask parts that are not visible? + paintingDesktop = desktop; effects->paintScreen( mask, region, d ); } - } -void DesktopGridEffect::paintSlide( int mask, QRegion region, const ScreenPaintData& data ) - { - /* - Transformations are done by remembering starting position of the change and the progress - of it, the destination is computed from the current desktop. Positions of desktops - are done using their topleft corner. - */ - QPoint destPos = desktopRect( effects->currentDesktop(), false ).topLeft(); - QPoint diffPos = destPos - slide_start_pos; - int w = 0; - int h = 0; - if( effects->optionRollOverDesktops()) + if( desktopNameAlignment ) { - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - w = x * displayWidth(); - h = y * displayHeight(); - // wrap around if shorter - if( diffPos.x() > 0 && diffPos.x() > w / 2 ) - diffPos.setX( diffPos.x() - w ); - if( diffPos.x() < 0 && abs( diffPos.x()) > w / 2 ) - diffPos.setX( diffPos.x() + w ); - if( diffPos.y() > 0 && diffPos.y() > h / 2 ) - diffPos.setY( diffPos.y() - h ); - if( diffPos.y() < 0 && abs( diffPos.y()) > h / 2 ) - diffPos.setY( diffPos.y() + h ); - } - QPoint currentPos = slide_start_pos + mTimeLine.value() * diffPos; - QSize displaySize( displayWidth(), displayHeight()); - QRegion currentRegion = QRect( currentPos, displaySize ); - if( effects->optionRollOverDesktops()) - { - currentRegion |= ( currentRegion & QRect( -w, 0, w, h )).translated( w, 0 ); - currentRegion |= ( currentRegion & QRect( 0, -h, w, h )).translated( 0, h ); - currentRegion |= ( currentRegion & QRect( w, 0, w, h )).translated( -w, 0 ); - currentRegion |= ( currentRegion & QRect( 0, h, w, h )).translated( 0, -h ); - } - bool do_sticky = true; - for( int desktop = 1; - desktop <= effects->numberOfDesktops(); - ++desktop ) - { - QRect rect = desktopRect( desktop, false ); - if( currentRegion.contains( rect )) // part of the desktop needs painting + double progress = timeline.value(); + QColor textColor( 255, 255, 255, 255 * progress ); + QColor bgColor( 0, 0, 0, 178 * progress ); // 70% + QFont f; + f.setBold( true ); + f.setPointSize( 12 ); + for( int screen = 0; screen < effects->numScreens(); screen++ ) { - painting_desktop = desktop; - slide_painting_sticky = do_sticky; - slide_painting_diff = rect.topLeft() - currentPos; - if( effects->optionRollOverDesktops()) + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + PaintClipper pc( screenGeom ); // TODO: Doesn't work in XRender for some reason? + for( int desktop = 1; desktop <= effects->numberOfDesktops(); desktop++ ) { - if( slide_painting_diff.x() > displayWidth()) - slide_painting_diff.setX( slide_painting_diff.x() - w ); - if( slide_painting_diff.x() < -displayWidth()) - slide_painting_diff.setX( slide_painting_diff.x() + w ); - if( slide_painting_diff.y() > displayHeight()) - slide_painting_diff.setY( slide_painting_diff.y() - h ); - if( slide_painting_diff.y() < -displayHeight()) - slide_painting_diff.setY( slide_painting_diff.y() + h ); + QPointF posTL( scalePos( screenGeom.topLeft(), desktop, screen )); + QPointF posBR( scalePos( screenGeom.bottomRight(), desktop, screen )); + QRect textArea( posTL.x(), posTL.y(), posBR.x() - posTL.x(), posBR.y() - posTL.y() ); + textArea.adjust( textArea.width() / 10, textArea.height() / 10, + -textArea.width() / 10, -textArea.height() / 10 ); + effects->paintTextWithBackground( effects->desktopName( desktop ), + textArea, textColor, bgColor, f, desktopNameAlignment ); } - do_sticky = false; // paint on-all-desktop windows only once - ScreenPaintData d = data; - d.xTranslate += slide_painting_diff.x(); - d.yTranslate += slide_painting_diff.y(); - // TODO mask parts that are not visible? - effects->paintScreen( mask, region, d ); } } } -void DesktopGridEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) - { - if( slide ) - { // don't move windows on all desktops (compensate screen transformation) - if( w->isOnAllDesktops()) // TODO also fix 'Workspace::movingClient' - { - data.xTranslate -= slide_painting_diff.x(); - data.yTranslate -= slide_painting_diff.y(); - } - } - else if( mTimeLine.value() != 0 ) - { - if( w == window_move ) - { - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - QRect desktop = desktopRect( painting_desktop, false ); - data.xTranslate += window_move_pos.x() * x - ( desktop.x() + w->x()); - data.yTranslate += window_move_pos.y() * y - ( desktop.y() + w->y()); - } - else if( painting_desktop != highlighted_desktop ) - data.brightness *= 0.7; - } - effects->paintWindow( w, mask, region, data ); - } - void DesktopGridEffect::postPaintScreen() { - if( slide ) - effects->addRepaintFull(); - if( activated ? mTimeLine.value() != 1 : mTimeLine.value() != 0 ) - effects->addRepaintFull(); // trigger next animation repaint + if( activated ? timeline.value() != 1 : timeline.value() != 0 ) + effects->addRepaintFull(); // Repaint during zoom + if( activated ) + { + for( int i = 0; i < effects->numberOfDesktops(); i++ ) + { + if( hoverTimeline[i].value() != 0.0 && hoverTimeline[i].value() != 1.0 ) + { // Repaint during soft highlighting + effects->addRepaintFull(); + break; + } + } + } effects->postPaintScreen(); } -// Gives a position of the given desktop when all desktops are arranged in a grid -QRect DesktopGridEffect::desktopRect( int desktop, bool scaled ) const - { - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - --desktop; // make it start with 0 - QRect rect; - if( orientation == Qt::Horizontal ) - rect = QRect(( desktop % x ) * displayWidth(), ( desktop / x ) * displayHeight(), - displayWidth(), displayHeight()); - else - rect = QRect(( desktop / y ) * displayWidth(), ( desktop % y ) * displayHeight(), - displayWidth(), displayHeight()); - if( !scaled ) - return rect; - QRect current = desktopRect( effects->currentDesktop(), false ); - double progress = mTimeLine.value(); - rect = QRect( qRound( interpolate( rect.x() - current.x(), rect.x() / double( x ), progress )), - qRound( interpolate( rect.y() - current.y(), rect.y() / double( y ), progress )), - qRound( interpolate( rect.width(), displayWidth() / double( x ), progress )), - qRound( interpolate( rect.height(), displayHeight() / double( y ), progress ))); - return rect; - } +//----------------------------------------------------------------------------- +// Window painting -int DesktopGridEffect::posToDesktop( const QPoint& pos ) const +void DesktopGridEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) { - for( int desktop = 1; // TODO could be perhaps optimized - desktop <= effects->numberOfDesktops(); - ++desktop ) + if( timeline.value() != 0 ) { - if( desktopRect( desktop, true ).contains( pos )) - return desktop; - } - return 0; - } - -QRect DesktopGridEffect::windowRect( EffectWindow* w ) const - { - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - if( w == window_move ) // it's being moved, return moved position - return QRect( window_move_pos, QSize( w->width() / x, w->height() / y )); - QRect desktop = desktopRect( w->isOnCurrentDesktop() - ? effects->currentDesktop() : w->desktop(), true ); - return QRect( desktop.x() + w->x() / x, desktop.y() + w->y() / y, - w->width() / x, w->height() / y ); - } - -EffectWindow* DesktopGridEffect::windowAt( const QPoint& pos, QRect* rect ) const - { - if( window_move != NULL && windowRect( window_move ).contains( pos )) - { - if( rect != NULL ) - *rect = windowRect( window_move ); - return window_move; // has special position and is on top - } - EffectWindowList windows = effects->stackingOrder(); - // qReverse() - EffectWindowList::Iterator begin = windows.begin(); - EffectWindowList::Iterator end = windows.end(); - --end; - while( begin < end ) - qSwap( *begin++, *end-- ); - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - foreach( EffectWindow* w, windows ) - { - // don't use windowRect(), take special care of on-all-desktop windows - QRect desktop = desktopRect( w->isOnAllDesktops() - ? posToDesktop( pos ) : w->desktop(), true ); - QRect r( desktop.x() + w->x() / x, desktop.y() + w->y() / y, - w->width() / x, w->height() / y ); - if( r.contains( pos )) + if( w->isOnDesktop( paintingDesktop )) { - if( rect != NULL ) - *rect = r; - return w; - } - } - return NULL; - } + w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + data.mask |= PAINT_WINDOW_TRANSFORMED; -void DesktopGridEffect::desktopChanged( int old ) - { - if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) - return; - if( activated ) - setActive( false ); - else - slideDesktopChanged( old ); - } - -void DesktopGridEffect::slideDesktopChanged( int old ) - { - if( !slideEnabled ) - return; - if( slide ) // old slide still in progress - { - QPoint diffPos = desktopRect( old, false ).topLeft() - slide_start_pos; - int w = 0; - int h = 0; - if( effects->optionRollOverDesktops()) - { - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - w = x * displayWidth(); - h = y * displayHeight(); - // wrap around if shorter - if( diffPos.x() > 0 && diffPos.x() > w / 2 ) - diffPos.setX( diffPos.x() - w ); - if( diffPos.x() < 0 && abs( diffPos.x()) > w / 2 ) - diffPos.setX( diffPos.x() + w ); - if( diffPos.y() > 0 && diffPos.y() > h / 2 ) - diffPos.setY( diffPos.y() - h ); - if( diffPos.y() < 0 && abs( diffPos.y()) > h / 2 ) - diffPos.setY( diffPos.y() + h ); - } - QPoint currentPos = slide_start_pos + mTimeLine.value() * diffPos; - QRegion currentRegion = QRect( currentPos, QSize( displayWidth(), displayHeight())); - if( effects->optionRollOverDesktops()) - { - currentRegion |= ( currentRegion & QRect( -w, 0, w, h )).translated( w, 0 ); - currentRegion |= ( currentRegion & QRect( 0, -h, w, h )).translated( 0, h ); - currentRegion |= ( currentRegion & QRect( w, 0, w, h )).translated( -w, 0 ); - currentRegion |= ( currentRegion & QRect( 0, h, w, h )).translated( 0, -h ); - } - QRect rect = desktopRect( effects->currentDesktop(), false ); - if( currentRegion.contains( rect )) - { // current position is in new current desktop (e.g. quickly changing back), - // don't do full progress - if( abs( currentPos.x() - rect.x()) > abs( currentPos.y() - rect.y())) - mTimeLine.setProgress(1 - abs( currentPos.x() - rect.x()) / double( displayWidth())); - else - mTimeLine.setProgress(1 - abs( currentPos.y() - rect.y()) / double( displayHeight())); - } - else // current position is not on current desktop, do full progress - mTimeLine.setProgress(0); - diffPos = rect.topLeft() - currentPos; - if( mTimeLine.value() <= 0 ) - { - // Compute starting point for this new move (given current and end positions) - slide_start_pos = rect.topLeft() - diffPos * 1 / ( 1 - mTimeLine.value() ); + // Split windows at screen edges + for( int screen = 0; screen < effects->numScreens(); screen++ ) + { + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + if( w->x() < screenGeom.x() ) + data.quads = data.quads.splitAtX( screenGeom.x() - w->x() ); + if( w->x() + w->width() > screenGeom.x() + screenGeom.width() ) + data.quads = data.quads.splitAtX( screenGeom.x() + screenGeom.width() - w->x() ); + if( w->y() < screenGeom.y() ) + data.quads = data.quads.splitAtY( screenGeom.y() - w->y() ); + if( w->y() + w->height() > screenGeom.y() + screenGeom.height() ) + data.quads = data.quads.splitAtY( screenGeom.y() + screenGeom.height() - w->y() ); + } } else - { // at the end, stop - slide = false; - mTimeLine.setProgress(0); + w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + } + effects->prePaintWindow( w, data, time ); + } + +void DesktopGridEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + if( timeline.value() != 0 ) + { + double xScale = data.xScale; + double yScale = data.yScale; + + data.brightness *= 1.0 - ( 0.3 * ( 1.0 - hoverTimeline[paintingDesktop - 1].value() )); + + for( int screen = 0; screen < effects->numScreens(); screen++ ) + { + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + + // Display all quads on the same screen on the same pass + WindowQuadList screenQuads; + foreach( WindowQuad quad, data.quads ) + { + QRect quadRect( + w->x() + quad.left(), w->y() + quad.top(), + quad.right() - quad.left(), quad.bottom() - quad.top() + ); + if( quadRect.intersects( screenGeom )) + screenQuads.append( quad ); + } + if( screenQuads.isEmpty() ) + continue; // Nothing is being displayed, don't bother + WindowPaintData d = data; + d.quads = screenQuads; + + QPointF newPos = scalePos( QPoint( w->x(), w->y() ), paintingDesktop, screen); + double progress = timeline.value(); + d.xScale = interpolate( 1, xScale * scale[screen], progress); + d.yScale = interpolate( 1, yScale * scale[screen], progress); + d.xTranslate += qRound( newPos.x() - w->x() ); + d.yTranslate += qRound( newPos.y() - w->y() ); + + if( effects->compositingType() == XRenderCompositing ) + { // More exact clipping as XRender displays the entire window instead of just the quad + QPointF screenPosF = scalePos( screenGeom.topLeft(), paintingDesktop ).toPoint(); + QPoint screenPos( + qRound( screenPosF.x() ), + qRound( screenPosF.y() ) + ); + QSize screenSize( + qRound( interpolate( screenGeom.width(), scaledSize[screen].width(), progress )), + qRound( interpolate( screenGeom.height(), scaledSize[screen].height(), progress )) + ); + PaintClipper pc( effects->clientArea( ScreenArea, screen, 0 ) & QRect( screenPos, screenSize )); + effects->paintWindow( w, mask, region, d ); + } + else + { + PaintClipper pc( effects->clientArea( ScreenArea, screen, 0 )); + effects->paintWindow( w, mask, region, d ); + } } } else + effects->paintWindow( w, mask, region, data ); + } + +//----------------------------------------------------------------------------- +// User interaction + +void DesktopGridEffect::windowClosed( EffectWindow* w ) + { + if( w == windowMove ) { - mTimeLine.setProgress(0); - slide_start_pos = desktopRect( old, false ).topLeft(); - slide = true; + effects->setElevatedWindow( windowMove, false ); + windowMove = NULL; } - effects->addRepaintFull(); - } - -void DesktopGridEffect::toggle() - { - setActive( !activated ); - } - -void DesktopGridEffect::setActive( bool active ) - { - if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) - return; - if( activated == active ) - return; - activated = active; - if( activated && mTimeLine.value() == 0 ) - setup(); - effects->addRepaintFull(); - } - -void DesktopGridEffect::setup() - { - keyboard_grab = effects->grabKeyboard( this ); - input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), - Qt::PointingHandCursor ); - effects->setActiveFullScreenEffect( this ); - setHighlightedDesktop( effects->currentDesktop()); - } - -void DesktopGridEffect::finish() - { - if( keyboard_grab ) - effects->ungrabKeyboard(); - keyboard_grab = false; - if( window_move != NULL ) - effects->setElevatedWindow( window_move, false ); - window_move = NULL; - effects->destroyInputWindow( input ); - effects->setActiveFullScreenEffect( 0 ); - effects->addRepaintFull(); // to get rid of highlight } void DesktopGridEffect::windowInputMouseEvent( Window, QEvent* e ) { - if( e->type() != QEvent::MouseMove + if(( e->type() != QEvent::MouseMove && e->type() != QEvent::MouseButtonPress && e->type() != QEvent::MouseButtonRelease ) + || timeline.value() != 1 ) // Block user input during animations return; QMouseEvent* me = static_cast< QMouseEvent* >( e ); if( e->type() == QEvent::MouseMove ) { - // highlight desktop under mouse int d = posToDesktop( me->pos()); - if( d != highlighted_desktop ) - setHighlightedDesktop( d ); - if( window_move != NULL ) // handle window moving - { - was_window_move = true; - // windowRect() handles window_move specially - effects->addRepaint( windowRect( window_move )); - window_move_pos = me->pos() + window_move_diff; - effects->addRepaint( windowRect( window_move )); + if( windowMove != NULL ) + { // Handle window moving + if( !wasWindowMove ) // Activate on move + effects->activateWindow( windowMove ); + wasWindowMove = true; + effects->moveWindow( windowMove, unscalePos( me->pos(), NULL ) + windowMoveDiff ); + // TODO: Window snap + if( d != highlightedDesktop && !windowMove->isOnAllDesktops() ) + effects->windowToDesktop( windowMove, d ); // Not true all desktop move } + if( d != highlightedDesktop ) // Highlight desktop + setHighlightedDesktop( d ); } if( e->type() == QEvent::MouseButtonPress ) { if( me->buttons() == Qt::LeftButton ) { QRect rect; - EffectWindow* w = windowAt( me->pos(), &rect ); + EffectWindow* w = windowAt( me->pos()); if( w != NULL && w->isMovable()) - { // prepare it for moving - window_move_pos = rect.topLeft(); - window_move_diff = window_move_pos - me->pos(); - window_move = w; - effects->setElevatedWindow( window_move, true ); + { // Prepare it for moving + windowMoveDiff = w->pos() - unscalePos( me->pos(), NULL ); + windowMove = w; } } - else if(( me->buttons() == Qt::MidButton || me->buttons() == Qt::RightButton ) && window_move == NULL ) + else if(( me->buttons() == Qt::MidButton || me->buttons() == Qt::RightButton ) && windowMove == NULL ) { EffectWindow* w = windowAt( me->pos()); if( w != NULL ) @@ -533,40 +320,17 @@ void DesktopGridEffect::windowInputMouseEvent( Window, QEvent* e ) } } } - if( e->type() == QEvent::MouseButtonRelease && me->buttons() == 0 ) + if( e->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton ) { - if( was_window_move ) + if( wasWindowMove ) + effects->activateWindow( windowMove ); // Just in case it was deactivated + else { - if( window_move != NULL ) - { - QRect rect = windowRect( window_move ); - int desktop = posToDesktop( rect.center()); - // to desktop's coordinates - rect.translate( -desktopRect( desktop, true ).topLeft()); - int x, y; - Qt::Orientation orientation; - effects->calcDesktopLayout( &x, &y, &orientation ); - effects->moveWindow( window_move, QPoint( rect.x() * x, rect.y() * y )); - effects->windowToDesktop( window_move, desktop ); - effects->setElevatedWindow( window_move, false ); - window_move = NULL; - } - } - if( !was_window_move && me->button() == Qt::LeftButton ) - { - effects->setCurrentDesktop( posToDesktop( me->pos())); + setCurrentDesktop( highlightedDesktop ); setActive( false ); } - was_window_move = false; - } - } - -void DesktopGridEffect::windowClosed( EffectWindow* w ) - { - if( w == window_move ) - { - effects->setElevatedWindow( window_move, false ); - window_move = NULL; + windowMove = NULL; + wasWindowMove = false; } } @@ -585,28 +349,24 @@ void DesktopGridEffect::grabbedKeyboardEvent( QKeyEvent* e ) if( desktop <= effects->numberOfDesktops()) { setHighlightedDesktop( desktop ); - effects->setCurrentDesktop( desktop ); + setCurrentDesktop( desktop ); setActive( false ); } return; } switch( e->key()) - { // wrap only on autorepeat + { // Wrap only on autorepeat case Qt::Key_Left: - setHighlightedDesktop( effects->desktopToLeft( highlighted_desktop, - !e->isAutoRepeat())); + setHighlightedDesktop( effects->desktopToLeft( highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Right: - setHighlightedDesktop( effects->desktopToRight( highlighted_desktop, - !e->isAutoRepeat())); + setHighlightedDesktop( effects->desktopToRight( highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Up: - setHighlightedDesktop( effects->desktopUp( highlighted_desktop, - !e->isAutoRepeat())); + setHighlightedDesktop( effects->desktopUp( highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Down: - setHighlightedDesktop( effects->desktopDown( highlighted_desktop, - !e->isAutoRepeat())); + setHighlightedDesktop( effects->desktopDown( highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Escape: setActive( false ); @@ -614,7 +374,7 @@ void DesktopGridEffect::grabbedKeyboardEvent( QKeyEvent* e ) case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Space: - effects->setCurrentDesktop( highlighted_desktop ); + setCurrentDesktop( highlightedDesktop ); setActive( false ); return; default: @@ -623,15 +383,6 @@ void DesktopGridEffect::grabbedKeyboardEvent( QKeyEvent* e ) } } -void DesktopGridEffect::setHighlightedDesktop( int d ) - { - if( d == highlighted_desktop || d <= 0 || d > effects->numberOfDesktops()) - return; - effects->addRepaint( desktopRect( highlighted_desktop, true )); - highlighted_desktop = d; - effects->addRepaint( desktopRect( highlighted_desktop, true )); - } - bool DesktopGridEffect::borderActivated( ElectricBorder border ) { if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) @@ -644,6 +395,257 @@ bool DesktopGridEffect::borderActivated( ElectricBorder border ) return false; } +//----------------------------------------------------------------------------- +// Helper functions + +// Transform a point to its position on the scaled grid +QPointF DesktopGridEffect::scalePos( const QPoint& pos, int desktop, int screen ) const + { + if( screen == -1 ) + screen = effects->screenNumber( pos ); + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + QPoint desktopCell; + if( orientation == Qt::Horizontal ) + { + desktopCell.setX(( desktop - 1 ) % gridSize.width() + 1 ); + desktopCell.setY(( desktop - 1 ) / gridSize.width() + 1 ); + } + else + { + desktopCell.setX(( desktop - 1 ) / gridSize.height() + 1 ); + desktopCell.setY(( desktop - 1 ) % gridSize.height() + 1 ); + } + + double progress = timeline.value(); + QPointF point( + interpolate( + ( + ( screenGeom.width() + unscaledBorder[screen] ) * ( desktopCell.x() - 1 ) + - ( screenGeom.width() + unscaledBorder[screen] ) * ( activeCell.x() - 1 ) + ) + pos.x(), + ( + ( scaledSize[screen].width() + border ) * ( desktopCell.x() - 1 ) + + scaledOffset[screen].x() + + ( pos.x() - screenGeom.x() ) * scale[screen] + ), + progress ), + interpolate( + ( + ( screenGeom.height() + unscaledBorder[screen] ) * ( desktopCell.y() - 1 ) + - ( screenGeom.height() + unscaledBorder[screen] ) * ( activeCell.y() - 1 ) + ) + pos.y(), + ( + ( scaledSize[screen].height() + border ) * ( desktopCell.y() - 1 ) + + scaledOffset[screen].y() + + ( pos.y() - screenGeom.y() ) * scale[screen] + ), + progress ) + ); + + return point; + } + +// Detransform a point to its position on the full grid +// TODO: Doesn't correctly interpolate (Final position is correct though), don't forget to copy to posToDesktop() +QPoint DesktopGridEffect::unscalePos( const QPoint& pos, int* desktop ) const + { + int screen = effects->screenNumber( pos ); + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + + //double progress = timeline.value(); + double scaledX = /*interpolate( + ( pos.x() - screenGeom.x() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.width() + unscaledBorder[screen] ) + activeCell.x() - 1,*/ + ( pos.x() - scaledOffset[screen].x() + double( border ) / 2.0 ) / ( scaledSize[screen].width() + border )/*, + progress )*/; + double scaledY = /*interpolate( + ( pos.y() - screenGeom.y() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.height() + unscaledBorder[screen] ) + activeCell.y() - 1,*/ + ( pos.y() - scaledOffset[screen].y() + double( border ) / 2.0 ) / ( scaledSize[screen].height() + border )/*, + progress )*/; + int gx = qBound( 0, int( scaledX ), gridSize.width() - 1 ); // Zero-based + int gy = qBound( 0, int( scaledY ), gridSize.height() - 1 ); + scaledX -= gx; + scaledY -= gy; + if( desktop != NULL ) + { + if( orientation == Qt::Horizontal ) + *desktop = gy * gridSize.width() + gx + 1; + else + *desktop = gx * gridSize.height() + gy + 1; + } + + return QPoint( + qBound( + screenGeom.x(), + qRound( + scaledX * ( screenGeom.width() + unscaledBorder[screen] ) + - unscaledBorder[screen] / 2.0 + + screenGeom.x() + ), + screenGeom.right() + ), + qBound( + screenGeom.y(), + qRound( + scaledY * ( screenGeom.height() + unscaledBorder[screen] ) + - unscaledBorder[screen] / 2.0 + + screenGeom.y() + ), + screenGeom.bottom() + ) + ); + } + +int DesktopGridEffect::posToDesktop( const QPoint& pos ) const + { // Copied from unscalePos() + int screen = effects->screenNumber( pos ); + QRect screenGeom = effects->clientArea( ScreenArea, screen, 0 ); + + //double progress = timeline.value(); + double scaledX = /*interpolate( + ( pos.x() - screenGeom.x() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.width() + unscaledBorder[screen] ) + activeCell.x() - 1,*/ + ( pos.x() - scaledOffset[screen].x() + double( border ) / 2.0 ) / ( scaledSize[screen].width() + border )/*, + progress )*/; + double scaledY = /*interpolate( + ( pos.y() - screenGeom.y() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.height() + unscaledBorder[screen] ) + activeCell.y() - 1,*/ + ( pos.y() - scaledOffset[screen].y() + double( border ) / 2.0 ) / ( scaledSize[screen].height() + border )/*, + progress )*/; + int gx = qBound( 0, int( scaledX ), gridSize.width() - 1 ); // Zero-based + int gy = qBound( 0, int( scaledY ), gridSize.height() - 1 ); + scaledX -= gx; + scaledY -= gy; + if( orientation == Qt::Horizontal ) + return gy * gridSize.width() + gx + 1; + return gx * gridSize.height() + gy + 1; + } + +EffectWindow* DesktopGridEffect::windowAt( QPoint pos ) const + { + // Get stacking order top first + EffectWindowList windows = effects->stackingOrder(); + EffectWindowList::Iterator begin = windows.begin(); + EffectWindowList::Iterator end = windows.end(); + --end; + while( begin < end ) + qSwap( *begin++, *end-- ); + + int desktop; + pos = unscalePos( pos, &desktop ); + foreach( EffectWindow* w, windows ) + if( w->isOnDesktop( desktop ) && w->geometry().contains( pos )) + return w; + return NULL; + } + +void DesktopGridEffect::setCurrentDesktop( int desktop ) + { + if( orientation == Qt::Horizontal ) + { + activeCell.setX(( desktop - 1 ) % gridSize.width() + 1 ); + activeCell.setY(( desktop - 1 ) / gridSize.width() + 1 ); + } + else + { + activeCell.setX(( desktop - 1 ) / gridSize.height() + 1 ); + activeCell.setY(( desktop - 1 ) % gridSize.height() + 1 ); + } + if( effects->currentDesktop() != desktop ) + effects->setCurrentDesktop( desktop ); + } + +void DesktopGridEffect::setHighlightedDesktop( int d ) + { + if( d == highlightedDesktop || d <= 0 || d > effects->numberOfDesktops() ) + return; + highlightedDesktop = d; + effects->addRepaintFull(); + } + +//----------------------------------------------------------------------------- +// Activation + +void DesktopGridEffect::toggle() + { + setActive( !activated ); + } + +void DesktopGridEffect::setActive( bool active ) + { + if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) + return; // Only one fullscreen effect at a time thanks + if( effects->numberOfDesktops() < 2 ) + return; // No point if there is only one desktop + if( activated == active ) + return; // Already in that state + + activated = active; + if( activated && timeline.value() == 0 ) + setup(); + effects->addRepaintFull(); + } + +void DesktopGridEffect::setup() + { + keyboardGrab = effects->grabKeyboard( this ); + input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(), + Qt::PointingHandCursor ); + effects->setActiveFullScreenEffect( this ); + setHighlightedDesktop( effects->currentDesktop() ); + + // Soft highlighting + hoverTimeline.clear(); + for( int i = 0; i < effects->numberOfDesktops(); i++ ) + { + TimeLine newTimeline; + newTimeline.setCurveShape( TimeLine::EaseInOutCurve ); + hoverTimeline.append( newTimeline ); + } + hoverTimeline[effects->currentDesktop() - 1].setProgress( 1.0 ); + + // We need these variables for every paint so lets cache them + effects->calcDesktopLayout( &gridSize.rwidth(), &gridSize.rheight(), &orientation ); + if( customLayout ) + { + gridSize.setWidth( ceil( effects->numberOfDesktops() / double( customLayoutRows ))); + gridSize.setHeight( customLayoutRows ); + } + setCurrentDesktop( effects->currentDesktop() ); + scale.clear(); + unscaledBorder.clear(); + scaledSize.clear(); + scaledOffset.clear(); + for( int i = 0; i < effects->numScreens(); i++ ) + { + QRect geom = effects->clientArea( ScreenArea, i, 0 ); + double sScale; + if( gridSize.width() > gridSize.height() ) + sScale = ( geom.width() - border * ( gridSize.width() + 1 )) / double( geom.width() * gridSize.width() ); + else + sScale = ( geom.height() - border * ( gridSize.height() + 1 )) / double( geom.height() * gridSize.height() ); + double sBorder = border / sScale; + QSizeF size( + double( geom.width() ) * sScale, + double( geom.height() ) * sScale + ); + QPointF offset( + geom.x() + ( geom.width() - size.width() * gridSize.width() - border * ( gridSize.width() - 1 )) / 2.0, + geom.y() + ( geom.height() - size.height() * gridSize.height() - border * ( gridSize.height() - 1 )) / 2.0 + ); + scale.append( sScale ); + unscaledBorder.append( sBorder ); + scaledSize.append( size ); + scaledOffset.append( offset ); + } + } + +void DesktopGridEffect::finish() + { + if( keyboardGrab ) + effects->ungrabKeyboard(); + keyboardGrab = false; + effects->destroyInputWindow( input ); + effects->setActiveFullScreenEffect( 0 ); + } + } // namespace #include "desktopgrid.moc" diff --git a/effects/desktopgrid.desktop b/effects/desktopgrid.desktop index 57fdfb2d14..63b9e58542 100644 --- a/effects/desktopgrid.desktop +++ b/effects/desktopgrid.desktop @@ -115,10 +115,10 @@ Comment[zh_TW]=將桌面以格狀佈局呈現的桌面切換器 Type=Service X-KDE-ServiceTypes=KWin/Effect -X-KDE-PluginInfo-Author=Luboš Luňák -X-KDE-PluginInfo-Email=l.lunak@kde.org +X-KDE-PluginInfo-Author=Luboš Luňák & Lucas Murray +X-KDE-PluginInfo-Email=l.lunak@kde.org & lmurray@undefinedfire.com X-KDE-PluginInfo-Name=kwin4_effect_desktopgrid -X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Version=0.2.0 X-KDE-PluginInfo-Category=Window Management X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL diff --git a/effects/desktopgrid.h b/effects/desktopgrid.h index c4ad29e13a..945755f741 100644 --- a/effects/desktopgrid.h +++ b/effects/desktopgrid.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak +Copyright (C) 2008 Lucas Murray 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 @@ -40,40 +41,50 @@ class DesktopGridEffect virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ); virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); virtual void windowClosed( EffectWindow* w ); - virtual void desktopChanged( int old ); virtual void windowInputMouseEvent( Window w, QEvent* e ); virtual void grabbedKeyboardEvent( QKeyEvent* e ); virtual bool borderActivated( ElectricBorder border ); private slots: void toggle(); private: - QRect desktopRect( int desktop, bool scaled ) const; + QPointF scalePos( const QPoint& pos, int desktop, int screen = -1 ) const; + QPoint unscalePos( const QPoint& pos, int* desktop = NULL ) const; int posToDesktop( const QPoint& pos ) const; - QRect windowRect( EffectWindow* w ) const; // returns always scaled - EffectWindow* windowAt( const QPoint& pos, QRect* rect = NULL ) const; + EffectWindow* windowAt( QPoint pos ) const; + void setCurrentDesktop( int desktop ); + void setHighlightedDesktop( int desktop ); void setActive( bool active ); void setup(); void finish(); - void paintSlide( int mask, QRegion region, const ScreenPaintData& data ); - void paintScreenDesktop( int desktop, int mask, QRegion region, ScreenPaintData data ); - void slideDesktopChanged( int old ); - void setHighlightedDesktop( int desktop ); - bool activated; - TimeLine mTimeLine; - int painting_desktop; - int highlighted_desktop; - Window input; - bool keyboard_grab; - bool was_window_move; - EffectWindow* window_move; - QPoint window_move_diff; - QPoint window_move_pos; - bool slideEnabled; - bool slide; - QPoint slide_start_pos; - bool slide_painting_sticky; - QPoint slide_painting_diff; + ElectricBorder borderActivate; + int zoomDuration; + int border; + Qt::Alignment desktopNameAlignment; + bool customLayout; + int customLayoutRows; + + bool activated; + TimeLine timeline; + int paintingDesktop; + int highlightedDesktop; + Window input; + bool keyboardGrab; + bool wasWindowMove; + EffectWindow* windowMove; + QPoint windowMoveDiff; + + // Soft highlighting + QList hoverTimeline; + + QSize gridSize; + Qt::Orientation orientation; + QPoint activeCell; + // Per screen variables + QList scale; // Because the border isn't a ratio each screen is different + QList unscaledBorder; + QList scaledSize; + QList scaledOffset; }; diff --git a/effects/desktopgrid_config.cpp b/effects/desktopgrid_config.cpp index c8a6e3deef..4b8034695e 100644 --- a/effects/desktopgrid_config.cpp +++ b/effects/desktopgrid_config.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2007 Rivo Laks +Copyright (C) 2008 Lucas Murray 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 @@ -19,123 +20,178 @@ along with this program. If not, see . *********************************************************************/ #include "desktopgrid_config.h" - #include -#include -#include +#include #include #include -#include -#include #include -#include -#include -#include -#include +#include #ifndef KDE_USE_FINAL KWIN_EFFECT_CONFIG_FACTORY #endif + namespace KWin { -DesktopGridEffectConfig::DesktopGridEffectConfig(QWidget* parent, const QVariantList& args) : - KCModule(EffectFactory::componentData(), parent, args) +DesktopGridEffectConfigForm::DesktopGridEffectConfigForm(QWidget* parent) : QWidget(parent) +{ + setupUi(this); +} + +DesktopGridEffectConfig::DesktopGridEffectConfig(QWidget* parent, const QVariantList& args) + : KCModule( EffectFactory::componentData(), parent, args ) { - kDebug() ; + m_ui = new DesktopGridEffectConfigForm( this ); - QVBoxLayout* layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout( this ); - mSlide = new QCheckBox(i18n("Animate desktop changes"), this); - connect(mSlide, SIGNAL(toggled(bool)), this, SLOT(changed())); - layout->addWidget(mSlide); + layout->addWidget( m_ui ); - QHBoxLayout* comboLayout = new QHBoxLayout(); - QLabel* label = new QLabel(i18n("Activate when cursor is at a specific edge " - "or corner of the screen:"), this); + m_actionCollection = new KActionCollection( this, componentData() ); + m_actionCollection->setConfigGroup( "DesktopGrid" ); + m_actionCollection->setConfigGlobal( true ); - mActivateCombo = new QComboBox; - mActivateCombo->addItem(i18n("Top")); - mActivateCombo->addItem(i18n("Top-right")); - mActivateCombo->addItem(i18n("Right")); - mActivateCombo->addItem(i18n("Bottom-right")); - mActivateCombo->addItem(i18n("Bottom")); - mActivateCombo->addItem(i18n("Bottom-left")); - mActivateCombo->addItem(i18n("Left")); - mActivateCombo->addItem(i18n("Top-left")); - mActivateCombo->addItem(i18n("None")); - connect(mActivateCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); - comboLayout->addWidget(label); - comboLayout->addWidget(mActivateCombo); - layout->addLayout(comboLayout); + KAction* a = (KAction*) m_actionCollection->addAction( "ShowDesktopGrid" ); + a->setText( i18n( "Show Desktop Grid" )); + a->setProperty( "isConfigurationAction", true ); + a->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F8 )); - KActionCollection* actionCollection = new KActionCollection( this, componentData() ); - KAction* show = static_cast(actionCollection->addAction( "ShowDesktopGrid" )); - show->setText( i18n("Show Desktop Grid" )); - show->setProperty("isConfigurationAction", true); - show->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F8 )); + m_ui->shortcutEditor->addCollection( m_actionCollection ); - mShortcutEditor = new KShortcutsEditor(actionCollection, this, - KShortcutsEditor::GlobalAction, KShortcutsEditor::LetterShortcutsDisallowed); - connect(mShortcutEditor, SIGNAL(keyChange()), this, SLOT(changed())); - layout->addWidget(mShortcutEditor); + m_ui->screenEdgeCombo->addItem( i18n( "Top" )); + m_ui->screenEdgeCombo->addItem( i18n( "Top-right" )); + m_ui->screenEdgeCombo->addItem( i18n( "Right" )); + m_ui->screenEdgeCombo->addItem( i18n( "Bottom-right" )); + m_ui->screenEdgeCombo->addItem( i18n( "Bottom" )); + m_ui->screenEdgeCombo->addItem( i18n( "Bottom-left" )); + m_ui->screenEdgeCombo->addItem( i18n( "Left" )); + m_ui->screenEdgeCombo->addItem( i18n( "Top-left" )); + m_ui->screenEdgeCombo->addItem( i18n( "None" )); - layout->addStretch(); + m_alignmentItems.append( Qt::Alignment( 0 )); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Disabled" )); + m_alignmentItems.append( Qt::AlignHCenter | Qt::AlignTop ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Top" )); + m_alignmentItems.append( Qt::AlignRight | Qt::AlignTop ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Top-right" )); + m_alignmentItems.append( Qt::AlignRight | Qt::AlignVCenter ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Right" )); + m_alignmentItems.append( Qt::AlignRight | Qt::AlignBottom ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Bottom-right" )); + m_alignmentItems.append( Qt::AlignHCenter | Qt::AlignBottom ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Bottom" )); + m_alignmentItems.append( Qt::AlignLeft | Qt::AlignBottom ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Bottom-left" )); + m_alignmentItems.append( Qt::AlignLeft | Qt::AlignVCenter ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Left" )); + m_alignmentItems.append( Qt::AlignLeft | Qt::AlignTop ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Top-left" )); + m_alignmentItems.append( Qt::AlignCenter ); + m_ui->desktopNameAlignmentCombo->addItem( i18n( "Center" )); + + connect( m_ui->screenEdgeCombo, SIGNAL( currentIndexChanged( int )), this, SLOT( changed() )); + connect( m_ui->zoomDurationSpin, SIGNAL( valueChanged( int )), this, SLOT( changed() )); + connect( m_ui->borderWidthSpin, SIGNAL( valueChanged( int )), this, SLOT( changed() )); + connect( m_ui->desktopNameAlignmentCombo, SIGNAL( currentIndexChanged( int )), this, SLOT( changed() )); + connect( m_ui->layoutBox, SIGNAL( stateChanged( int )), this, SLOT( changed() )); + connect( m_ui->layoutBox, SIGNAL( stateChanged( int )), this, SLOT( layoutSelectionChanged() )); + connect( m_ui->layoutRowsSpin, SIGNAL( valueChanged( int )), this, SLOT( changed() )); + connect( m_ui->shortcutEditor, SIGNAL( keyChange() ), this, SLOT( changed() )); + + load(); } DesktopGridEffectConfig::~DesktopGridEffectConfig() { - kDebug(); - // Undo (only) unsaved changes to global key shortcuts - mShortcutEditor->undoChanges(); + // If save() is called undoChanges() has no effect + m_ui->shortcutEditor->undoChanges(); } void DesktopGridEffectConfig::load() { - kDebug(); KCModule::load(); - KConfigGroup conf = EffectsHandler::effectConfig("DesktopGrid"); - mSlide->setChecked(conf.readEntry("Slide", true)); - - int activateBorder = conf.readEntry("BorderActivate", (int)ElectricNone); - if(activateBorder == (int)ElectricNone) + KConfigGroup conf = EffectsHandler::effectConfig( "DesktopGrid" ); + + int activateBorder = conf.readEntry( "BorderActivate", int( ElectricNone )); + if( activateBorder == int( ElectricNone )) activateBorder--; - mActivateCombo->setCurrentIndex(activateBorder); + m_ui->screenEdgeCombo->setCurrentIndex( activateBorder ); + + m_ui->zoomDurationSpin->setValue( conf.readEntry( "ZoomDuration", 500 )); + m_ui->borderWidthSpin->setValue( conf.readEntry( "BorderWidth", 10 )); + + Qt::Alignment alignment = Qt::Alignment( conf.readEntry( "DesktopNameAlignment", 0 )); + m_ui->desktopNameAlignmentCombo->setCurrentIndex( m_alignmentItems.indexOf( alignment )); + + if( conf.readEntry( "CustomLayout", false )) + m_ui->layoutBox->setCheckState( Qt::Checked ); + else + m_ui->layoutBox->setCheckState( Qt::Unchecked ); + layoutSelectionChanged(); + + m_ui->layoutRowsSpin->setValue( conf.readEntry( "CustomLayoutRows", 2 )); emit changed(false); } void DesktopGridEffectConfig::save() { - kDebug() ; KCModule::save(); - KConfigGroup conf = EffectsHandler::effectConfig("DesktopGrid"); - conf.writeEntry("Slide", mSlide->isChecked()); + KConfigGroup conf = EffectsHandler::effectConfig( "DesktopGrid" ); + + int activateBorder = m_ui->screenEdgeCombo->currentIndex(); + if( activateBorder == int( ELECTRIC_COUNT )) + activateBorder = int( ElectricNone ); + conf.writeEntry( "BorderActivate", activateBorder ); + + conf.writeEntry( "ZoomDuration", m_ui->zoomDurationSpin->value() ); + conf.writeEntry( "BorderWidth", m_ui->borderWidthSpin->value() ); + + int alignment = m_ui->desktopNameAlignmentCombo->currentIndex(); + alignment = int( m_alignmentItems[alignment] ); + conf.writeEntry( "DesktopNameAlignment", alignment ); + + conf.writeEntry( "CustomLayout", m_ui->layoutBox->checkState() == Qt::Checked ? 1 : 0 ); + conf.writeEntry( "CustomLayoutRows", m_ui->layoutRowsSpin->value() ); + + m_ui->shortcutEditor->save(); - int activateBorder = mActivateCombo->currentIndex(); - if(activateBorder == (int)ELECTRIC_COUNT) - activateBorder = (int)ElectricNone; - conf.writeEntry("BorderActivate", activateBorder); conf.sync(); - mShortcutEditor->save(); // undo() will restore to this state from now on - emit changed(false); EffectsHandler::sendReloadMessage( "desktopgrid" ); } void DesktopGridEffectConfig::defaults() { - kDebug() ; - mSlide->setChecked(true); - mActivateCombo->setCurrentIndex( (int)ElectricNone -1 ); - mShortcutEditor->allDefault(); + m_ui->screenEdgeCombo->setCurrentIndex( int( ElectricNone - 1 )); + m_ui->zoomDurationSpin->setValue( 500 ); + m_ui->borderWidthSpin->setValue( 10 ); + m_ui->desktopNameAlignmentCombo->setCurrentIndex( 0 ); + m_ui->layoutBox->setCheckState( Qt::Unchecked ); + m_ui->layoutRowsSpin->setValue( 2 ); + m_ui->shortcutEditor->allDefault(); emit changed(true); } +void DesktopGridEffectConfig::layoutSelectionChanged() + { + if( m_ui->layoutBox->checkState() == Qt::Checked ) + { + m_ui->layoutRowsLabel->setEnabled( true ); + m_ui->layoutRowsSpin->setEnabled( true ); + } + else + { + m_ui->layoutRowsLabel->setEnabled( false ); + m_ui->layoutRowsSpin->setEnabled( false ); + } + } + } // namespace #include "desktopgrid_config.moc" diff --git a/effects/desktopgrid_config.h b/effects/desktopgrid_config.h index 6c91021ffe..938fd208dd 100644 --- a/effects/desktopgrid_config.h +++ b/effects/desktopgrid_config.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2007 Rivo Laks +Copyright (C) 2008 Lucas Murray 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 @@ -23,13 +24,18 @@ along with this program. If not, see . #include -class QComboBox; -class QCheckBox; -class KShortcutsEditor; +#include "ui_desktopgrid_config.h" namespace KWin { +class DesktopGridEffectConfigForm : public QWidget, public Ui::DesktopGridEffectConfigForm +{ + Q_OBJECT + public: + explicit DesktopGridEffectConfigForm(QWidget* parent); +}; + class DesktopGridEffectConfig : public KCModule { Q_OBJECT @@ -42,10 +48,13 @@ class DesktopGridEffectConfig : public KCModule virtual void load(); virtual void defaults(); + private slots: + void layoutSelectionChanged(); + private: - QCheckBox* mSlide; - QComboBox* mActivateCombo; - KShortcutsEditor *mShortcutEditor; + DesktopGridEffectConfigForm* m_ui; + KActionCollection* m_actionCollection; + QList m_alignmentItems; }; } // namespace diff --git a/effects/desktopgrid_config.ui b/effects/desktopgrid_config.ui new file mode 100644 index 0000000000..2effe84c19 --- /dev/null +++ b/effects/desktopgrid_config.ui @@ -0,0 +1,178 @@ + + KWin::DesktopGridEffectConfigForm + + + + 0 + 0 + 508 + 221 + + + + + + + Appearance + + + + + + Zoom &duration: + + + zoomDurationSpin + + + + + + + msec + + + 5000 + + + 500 + + + + + + + &Border width: + + + borderWidthSpin + + + + + + + pixels + + + 100 + + + 10 + + + + + + + Desktop &name alignment: + + + desktopNameAlignmentCombo + + + + + + + + + + Custom grid &layout + + + + + + + Number of &rows: + + + layoutRowsSpin + + + + + + + rows + + + 1 + + + 20 + + + 2 + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + Activation + + + + + + Active screen &edge: + + + screenEdgeCombo + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + KWin::GlobalShortcutsEditor + QWidget +
kwineffects.h
+ 1 +
+
+ + zoomDurationSpin + borderWidthSpin + desktopNameAlignmentCombo + layoutBox + layoutRowsSpin + + + +
diff --git a/effects/slide.cpp b/effects/slide.cpp new file mode 100644 index 0000000000..11c7a4c858 --- /dev/null +++ b/effects/slide.cpp @@ -0,0 +1,266 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak +Copyright (C) 2008 Lucas Murray + +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 "slide.h" + +#include + +namespace KWin +{ + +KWIN_EFFECT( slide, SlideEffect ) + +SlideEffect::SlideEffect() + : slide( false ) + { + mTimeLine.setCurveShape(TimeLine::EaseInOutCurve); + } + +void SlideEffect::prePaintScreen( ScreenPrePaintData& data, int time ) + { + if( slide ) + { + mTimeLine.addTime(time); + + // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once, + // so with normal screen painting second screen paint would erase parts of the first paint + if( mTimeLine.value() != 1 ) + data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST; + else + { + slide = false; + mTimeLine.setProgress(0); + } + } + effects->prePaintScreen( data, time ); + } + +void SlideEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ) + { + if( slide ) + { + if( w->isOnAllDesktops()) + { + if( slide_painting_sticky ) + data.setTransformed(); + else + w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + } + else if( w->isOnDesktop( painting_desktop )) + w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + else + w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP ); + } + effects->prePaintWindow( w, data, time ); + } + +void SlideEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data ) + { + if( mTimeLine.value() == 0 ) + { + effects->paintScreen( mask, region, data ); + return; + } + + /* + Transformations are done by remembering starting position of the change and the progress + of it, the destination is computed from the current desktop. Positions of desktops + are done using their topleft corner. + */ + QPoint destPos = desktopRect( effects->currentDesktop(), false ).topLeft(); + QPoint diffPos = destPos - slide_start_pos; + int w = 0; + int h = 0; + if( effects->optionRollOverDesktops()) + { + int x, y; + Qt::Orientation orientation; + effects->calcDesktopLayout( &x, &y, &orientation ); + w = x * displayWidth(); + h = y * displayHeight(); + // wrap around if shorter + if( diffPos.x() > 0 && diffPos.x() > w / 2 ) + diffPos.setX( diffPos.x() - w ); + if( diffPos.x() < 0 && abs( diffPos.x()) > w / 2 ) + diffPos.setX( diffPos.x() + w ); + if( diffPos.y() > 0 && diffPos.y() > h / 2 ) + diffPos.setY( diffPos.y() - h ); + if( diffPos.y() < 0 && abs( diffPos.y()) > h / 2 ) + diffPos.setY( diffPos.y() + h ); + } + QPoint currentPos = slide_start_pos + mTimeLine.value() * diffPos; + QSize displaySize( displayWidth(), displayHeight()); + QRegion currentRegion = QRect( currentPos, displaySize ); + if( effects->optionRollOverDesktops()) + { + currentRegion |= ( currentRegion & QRect( -w, 0, w, h )).translated( w, 0 ); + currentRegion |= ( currentRegion & QRect( 0, -h, w, h )).translated( 0, h ); + currentRegion |= ( currentRegion & QRect( w, 0, w, h )).translated( -w, 0 ); + currentRegion |= ( currentRegion & QRect( 0, h, w, h )).translated( 0, -h ); + } + bool do_sticky = true; + for( int desktop = 1; + desktop <= effects->numberOfDesktops(); + ++desktop ) + { + QRect rect = desktopRect( desktop, false ); + if( currentRegion.contains( rect )) // part of the desktop needs painting + { + painting_desktop = desktop; + slide_painting_sticky = do_sticky; + slide_painting_diff = rect.topLeft() - currentPos; + if( effects->optionRollOverDesktops()) + { + if( slide_painting_diff.x() > displayWidth()) + slide_painting_diff.setX( slide_painting_diff.x() - w ); + if( slide_painting_diff.x() < -displayWidth()) + slide_painting_diff.setX( slide_painting_diff.x() + w ); + if( slide_painting_diff.y() > displayHeight()) + slide_painting_diff.setY( slide_painting_diff.y() - h ); + if( slide_painting_diff.y() < -displayHeight()) + slide_painting_diff.setY( slide_painting_diff.y() + h ); + } + do_sticky = false; // paint on-all-desktop windows only once + ScreenPaintData d = data; + d.xTranslate += slide_painting_diff.x(); + d.yTranslate += slide_painting_diff.y(); + // TODO mask parts that are not visible? + effects->paintScreen( mask, region, d ); + } + } + } + +void SlideEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ) + { + if( slide ) + { // don't move windows on all desktops (compensate screen transformation) + if( w->isOnAllDesktops()) // TODO also fix 'Workspace::movingClient' + { + data.xTranslate -= slide_painting_diff.x(); + data.yTranslate -= slide_painting_diff.y(); + } + } + effects->paintWindow( w, mask, region, data ); + } + +void SlideEffect::postPaintScreen() + { + if( slide ) + effects->addRepaintFull(); + effects->postPaintScreen(); + } + +// Gives a position of the given desktop when all desktops are arranged in a grid +QRect SlideEffect::desktopRect( int desktop, bool scaled ) const + { + int x, y; + Qt::Orientation orientation; + effects->calcDesktopLayout( &x, &y, &orientation ); + --desktop; // make it start with 0 + QRect rect; + if( orientation == Qt::Horizontal ) + rect = QRect(( desktop % x ) * displayWidth(), ( desktop / x ) * displayHeight(), + displayWidth(), displayHeight()); + else + rect = QRect(( desktop / y ) * displayWidth(), ( desktop % y ) * displayHeight(), + displayWidth(), displayHeight()); + if( !scaled ) + return rect; + QRect current = desktopRect( effects->currentDesktop(), false ); + double progress = mTimeLine.value(); + rect = QRect( qRound( interpolate( rect.x() - current.x(), rect.x() / double( x ), progress )), + qRound( interpolate( rect.y() - current.y(), rect.y() / double( y ), progress )), + qRound( interpolate( rect.width(), displayWidth() / double( x ), progress )), + qRound( interpolate( rect.height(), displayHeight() / double( y ), progress ))); + return rect; + } + +void SlideEffect::desktopChanged( int old ) + { + if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) + return; + + if( slide ) // old slide still in progress + { + QPoint diffPos = desktopRect( old, false ).topLeft() - slide_start_pos; + int w = 0; + int h = 0; + if( effects->optionRollOverDesktops()) + { + int x, y; + Qt::Orientation orientation; + effects->calcDesktopLayout( &x, &y, &orientation ); + w = x * displayWidth(); + h = y * displayHeight(); + // wrap around if shorter + if( diffPos.x() > 0 && diffPos.x() > w / 2 ) + diffPos.setX( diffPos.x() - w ); + if( diffPos.x() < 0 && abs( diffPos.x()) > w / 2 ) + diffPos.setX( diffPos.x() + w ); + if( diffPos.y() > 0 && diffPos.y() > h / 2 ) + diffPos.setY( diffPos.y() - h ); + if( diffPos.y() < 0 && abs( diffPos.y()) > h / 2 ) + diffPos.setY( diffPos.y() + h ); + } + QPoint currentPos = slide_start_pos + mTimeLine.value() * diffPos; + QRegion currentRegion = QRect( currentPos, QSize( displayWidth(), displayHeight())); + if( effects->optionRollOverDesktops()) + { + currentRegion |= ( currentRegion & QRect( -w, 0, w, h )).translated( w, 0 ); + currentRegion |= ( currentRegion & QRect( 0, -h, w, h )).translated( 0, h ); + currentRegion |= ( currentRegion & QRect( w, 0, w, h )).translated( -w, 0 ); + currentRegion |= ( currentRegion & QRect( 0, h, w, h )).translated( 0, -h ); + } + QRect rect = desktopRect( effects->currentDesktop(), false ); + if( currentRegion.contains( rect )) + { // current position is in new current desktop (e.g. quickly changing back), + // don't do full progress + if( abs( currentPos.x() - rect.x()) > abs( currentPos.y() - rect.y())) + mTimeLine.setProgress(1 - abs( currentPos.x() - rect.x()) / double( displayWidth())); + else + mTimeLine.setProgress(1 - abs( currentPos.y() - rect.y()) / double( displayHeight())); + } + else // current position is not on current desktop, do full progress + mTimeLine.setProgress(0); + diffPos = rect.topLeft() - currentPos; + if( mTimeLine.value() <= 0 ) + { + // Compute starting point for this new move (given current and end positions) + slide_start_pos = rect.topLeft() - diffPos * 1 / ( 1 - mTimeLine.value() ); + } + else + { // at the end, stop + slide = false; + mTimeLine.setProgress(0); + } + } + else + { + mTimeLine.setProgress(0); + slide_start_pos = desktopRect( old, false ).topLeft(); + slide = true; + } + effects->addRepaintFull(); + } + +} // namespace + +#include "slide.moc" diff --git a/effects/slide.desktop b/effects/slide.desktop new file mode 100644 index 0000000000..e657913182 --- /dev/null +++ b/effects/slide.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Slide +Icon=preferences-system-windows-effect-slide +Comment=Slide windows when switching desktops + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Luboš Luňák +X-KDE-PluginInfo-Email=l.lunak@kde.org +X-KDE-PluginInfo-Name=kwin4_effect_slide +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Appearance +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=50 diff --git a/effects/slide.h b/effects/slide.h new file mode 100644 index 0000000000..7ad68382bf --- /dev/null +++ b/effects/slide.h @@ -0,0 +1,56 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2007 Lubos Lunak +Copyright (C) 2008 Lucas Murray + +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 . +*********************************************************************/ + +#ifndef KWIN_SLIDE_H +#define KWIN_SLIDE_H + +#include +#include + +namespace KWin +{ + +class SlideEffect + : public QObject, public Effect + { + Q_OBJECT + public: + SlideEffect(); + virtual void prePaintScreen( ScreenPrePaintData& data, int time ); + virtual void paintScreen( int mask, QRegion region, ScreenPaintData& data ); + virtual void postPaintScreen(); + virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time ); + virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data ); + virtual void desktopChanged( int old ); + private: + QRect desktopRect( int desktop, bool scaled ) const; + TimeLine mTimeLine; + int painting_desktop; + bool slide; + QPoint slide_start_pos; + bool slide_painting_sticky; + QPoint slide_painting_diff; + + }; + +} // namespace + +#endif