/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 "desktopchangeosd.h" #include #include "workspace.h" #include #include #include #include #include #include #include #include #include namespace KWin { DesktopChangeOSD::DesktopChangeOSD( Workspace* ws ) : QGraphicsView() , m_wspace( ws ) , m_scene( 0 ) , m_active( false ) , m_show( false ) , m_delayTime( 0 ) , m_textOnly( false ) { setWindowFlags( Qt::X11BypassWindowManagerHint ); setFrameStyle( QFrame::NoFrame ); viewport()->setAutoFillBackground( false ); setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); setAttribute( Qt::WA_TranslucentBackground ); m_frame.setImagePath( "dialogs/background" ); m_frame.setCacheAllRenderedFrames( true ); m_frame.setEnabledBorders( Plasma::FrameSvg::AllBorders ); m_item_frame.setImagePath( "widgets/pager" ); m_item_frame.setCacheAllRenderedFrames( true ); m_item_frame.setEnabledBorders( Plasma::FrameSvg::AllBorders ); m_delayedHideTimer.setSingleShot( true ); connect( &m_delayedHideTimer, SIGNAL(timeout()), this, SLOT(hide()) ); m_scene = new QGraphicsScene( 0 ); setScene( m_scene ); reconfigure(); m_scene->addItem( new DesktopChangeText( m_wspace ) ); } DesktopChangeOSD::~DesktopChangeOSD() { delete m_scene; } void DesktopChangeOSD::reconfigure() { KSharedConfigPtr c(KGlobal::config()); const KConfigGroup cg = c->group( "PopupInfo" ); m_show = cg.readEntry( "ShowPopup", false ); m_delayTime = cg.readEntry( "PopupHideDelay", 1000 ); m_textOnly = cg.readEntry( "TextOnly", false ); numberDesktopsChanged(); } void DesktopChangeOSD::desktopChanged( int old ) { if( !m_show ) return; // we have to stop in case the old desktop does not exist anymore if( old > m_wspace->numberOfDesktops() ) return; // calculate where icons have to be shown QPoint diff = m_wspace->desktopGridCoords( m_wspace->currentDesktop() ) - m_wspace->desktopGridCoords( old ); QHash< int, DesktopChangeItem::Arrow > hash = QHash< int, DesktopChangeItem::Arrow>(); int desktop = old; int target = m_wspace->currentDesktop(); int x = diff.x(); int y = diff.y(); if( y >= 0 ) { // first go in x direction, then in y while( desktop != target ) { if( x != 0 ) { if( x < 0 ) { x++; hash.insert( desktop, DesktopChangeItem::LEFT ); desktop = m_wspace->desktopToLeft( desktop ); } else { x--; hash.insert( desktop, DesktopChangeItem::RIGHT ); desktop = m_wspace->desktopToRight( desktop ); } continue; } y--; hash.insert( desktop, DesktopChangeItem::DOWN ); desktop = m_wspace->desktopBelow( desktop ); } } else { // first go in y direction, then in x while( target != desktop ) { if( y != 0 ) { // only go upward y++; hash.insert( desktop, DesktopChangeItem::UP ); desktop = m_wspace->desktopAbove( desktop ); continue; } if( x != 0 ) { if( x < 0 ) { x++; hash.insert( desktop, DesktopChangeItem::LEFT ); desktop = m_wspace->desktopToLeft( desktop ); } else { x--; hash.insert( desktop, DesktopChangeItem::RIGHT ); desktop = m_wspace->desktopToRight( desktop ); } } } } // now we know which desktop has to show an arrow -> set the arrow for each desktop int numberOfArrows = qAbs( diff.x() ) + qAbs( diff.y() ); foreach( QGraphicsItem* it, m_scene->items() ) { DesktopChangeItem* item = qgraphicsitem_cast< DesktopChangeItem* >( it ); if( item ) { if( hash.contains( item->desktop() ) ) { QPoint distance = m_wspace->desktopGridCoords( m_wspace->currentDesktop() ) - m_wspace->desktopGridCoords( item->desktop() ); int desktopDistance = numberOfArrows - ( qAbs( distance.x() ) + qAbs( distance.y() ) ); int start = m_delayTime/numberOfArrows * desktopDistance - m_delayTime*0.15f; int stop = m_delayTime/numberOfArrows * (desktopDistance + 1) + m_delayTime*0.15f; start = qMax( start, 0 ); item->setArrow( hash[ item->desktop() ], start, stop ); } else { item->setArrow( DesktopChangeItem::NONE, 0, 0 ); } if( old != m_wspace->currentDesktop() ) { if( item->desktop() == m_wspace->currentDesktop() ) item->startDesktopHighLightAnimation( m_delayTime * 0.33 ); if( m_active && item->desktop() == old ) item->stopDesktopHighLightAnimation(); } } } if( m_active ) { // for text only we need to resize if( m_textOnly ) resize(); // already active - just update and reset timer update(); } else { m_active = true; resize(); show(); raise(); } // Set a zero inputmask, effectively making clicks go "through" the popup // For those who impatiently wait to click on a dialog behind the it XShapeCombineRectangles( display(), winId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted ); m_delayedHideTimer.start( m_delayTime ); } void DesktopChangeOSD::hideEvent( QHideEvent* ) { m_delayedHideTimer.stop(); m_active = false; } void DesktopChangeOSD::drawBackground( QPainter* painter, const QRectF& rect ) { painter->save(); painter->setCompositionMode( QPainter::CompositionMode_Source ); qreal left, top, right, bottom; m_frame.getMargins( left, top, right, bottom ); m_frame.paintFrame( painter, rect.adjusted( -left, -top, right, bottom ) ); painter->restore(); } void DesktopChangeOSD::numberDesktopsChanged() { foreach( QGraphicsItem* it, m_scene->items() ) { DesktopChangeItem* item = qgraphicsitem_cast(it); if( item ) { m_scene->removeItem( item ); } } if( !m_textOnly ) { for( int i=1; i<=m_wspace->numberOfDesktops(); i++ ) { DesktopChangeItem* item = new DesktopChangeItem( m_wspace, this, i ); m_scene->addItem( item ); } } } void DesktopChangeOSD::resize() { QRect screenRect = m_wspace->clientArea( ScreenArea, m_wspace->activeScreen(), m_wspace->currentDesktop() ); QRect fullRect = m_wspace->clientArea( FullArea, m_wspace->activeScreen(), m_wspace->currentDesktop() ); qreal left, top, right, bottom; m_frame.getMargins( left, top, right, bottom ); QSize desktopGridSize = m_wspace->desktopGridSize(); float itemWidth = fullRect.width()*0.1f; float itemHeight = fullRect.height()*0.1f; // 2 px distance between each desktop + each desktop a width of 5 % of full screen + borders float width = (desktopGridSize.width()-1)*2 + desktopGridSize.width()*itemWidth + left + right; float height = (desktopGridSize.height()-1)*2 + top + bottom; // bound width between ten and 33 percent of active screen float tempWidth = qBound( screenRect.width()*0.25f, width, screenRect.width()*0.5f ); if( tempWidth != width ) { // have to adjust the height width = tempWidth; itemWidth = (width - (desktopGridSize.width()-1)*2 - left - right)/desktopGridSize.width(); itemHeight = itemWidth*(float)((float)fullRect.height()/(float)fullRect.width()); } height += itemHeight*desktopGridSize.height(); height += fontMetrics().height() + 4; // we do not increase height, but it's bound to a third of screen height float tempHeight = qMin( height, screenRect.height()*0.5f ); float itemOffset = 0.0f; if( tempHeight != height ) { // have to adjust item width height = tempHeight; itemHeight = (height - (fontMetrics().height() + 4) - top - bottom - (desktopGridSize.height()-1)*2)/ desktopGridSize.height(); itemOffset = itemWidth; itemWidth = itemHeight*(float)((float)fullRect.width()/(float)fullRect.height()); itemOffset -= itemWidth; itemOffset *= (float)desktopGridSize.width()*0.5f; } // set size to the desktop name if the "pager" is not shown if( m_textOnly ) { height = fontMetrics().height() + 4 + top + bottom; width = fontMetrics().boundingRect( m_wspace->desktopName( m_wspace->currentDesktop() ) ).width() + 4 + left + right; } QRect rect = QRect( screenRect.x() + (screenRect.width()-width)/2, screenRect.y() + (screenRect.height()-height)/2, width, height ); setGeometry( rect ); m_scene->setSceneRect( 0, 0, width, height ); m_frame.resizeFrame( QSize( width, height ) ); if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) { // blur background Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame.mask()); Plasma::WindowEffects::overrideShadow(winId(), true); } else { // do not trim to mask with compositing enabled, otherwise shadows are cropped setMask( m_frame.mask() ); } // resize item frame m_item_frame.setElementPrefix( "normal" ); m_item_frame.resizeFrame( QSize( itemWidth, itemHeight ) ); m_item_frame.setElementPrefix( "hover" ); m_item_frame.resizeFrame( QSize( itemWidth, itemHeight ) ); // reset the items foreach( QGraphicsItem* it, m_scene->items() ) { DesktopChangeItem* item = qgraphicsitem_cast(it); if( item ) { item->setWidth( itemWidth ); item->setHeight( itemHeight ); QPoint coords = m_wspace->desktopGridCoords( item->desktop() ); item->setPos( left + itemOffset + coords.x()*(itemWidth+2), top + fontMetrics().height() + 4 + coords.y()*(itemHeight+4) ); } DesktopChangeText* text = qgraphicsitem_cast(it); if( text ) { text->setPos( left, top ); text->setWidth( width - left - right ); if( m_textOnly ) text->setHeight( fontMetrics().height() + 4 ); else text->setHeight( fontMetrics().height() ); } } } //******************************* // DesktopChangeText //******************************* DesktopChangeText::DesktopChangeText( Workspace* ws ) : QGraphicsItem() , m_wspace( ws ) , m_width( 0.0f ) , m_height( 0.0f ) { } DesktopChangeText::~DesktopChangeText() { } QRectF DesktopChangeText::boundingRect() const { return QRectF( 0, 0, m_width, m_height ); } void DesktopChangeText::paint( QPainter* painter, const QStyleOptionGraphicsItem* , QWidget* ) { painter->setPen( Plasma::Theme::defaultTheme()->color( Plasma::Theme::TextColor ) ); painter->drawText( boundingRect(), Qt::AlignCenter | Qt:: AlignVCenter, m_wspace->desktopName( m_wspace->currentDesktop() ) ); } //******************************* // DesktopChangeItem //******************************* DesktopChangeItem::DesktopChangeItem( Workspace* ws, DesktopChangeOSD* parent, int desktop ) : QGraphicsItem() , m_wspace( ws ) , m_parent( parent ) , m_desktop( desktop ) , m_width( 0.0f ) , m_height( 0.0f ) , m_arrow( NONE ) , m_arrowShown( false ) , m_fadeInArrow( false ) , m_fadeInHighLight( false ) , m_arrowValue( 0.0 ) , m_highLightValue( 0.0 ) { m_delayed_show_arrow_timer.setSingleShot( true ); m_delayed_hide_arrow_timer.setSingleShot( true ); connect( &m_delayed_show_arrow_timer, SIGNAL(timeout()), this, SLOT(showArrow())); connect( &m_delayed_hide_arrow_timer, SIGNAL(timeout()), this, SLOT(hideArrow())); } DesktopChangeItem::~DesktopChangeItem() { } QRectF DesktopChangeItem::boundingRect() const { return QRectF( 0, 0, m_width, m_height ); } void DesktopChangeItem::setArrow( Arrow arrow, int start_delay, int hide_delay ) { // stop timers m_delayed_show_arrow_timer.stop(); m_delayed_hide_arrow_timer.stop(); QPropertyAnimation *arrowAnimation = m_arrowAnimation.data(); if( arrowAnimation ) { arrowAnimation->stop(); m_arrowAnimation.clear(); } m_arrowShown = false; m_arrow = arrow; if( m_arrow != NONE ) { m_delayed_show_arrow_timer.start( start_delay ); m_delayed_hide_arrow_timer.start( hide_delay ); } } qreal DesktopChangeItem::arrowValue() const { qCritical() << __func__ << m_arrowValue; return m_arrowValue; } qreal DesktopChangeItem::highLightValue() const { return m_highLightValue; } void DesktopChangeItem::setArrowValue( qreal value ) { m_arrowValue = value; update(); } void DesktopChangeItem::setHighLightValue( qreal value ) { m_highLightValue = value; update(); } void DesktopChangeItem::showArrow() { m_arrowShown = true; QPropertyAnimation *arrowAnimation = m_arrowAnimation.data(); if( !arrowAnimation ) { arrowAnimation = new QPropertyAnimation( this, "arrowValue" ); arrowAnimation->setDuration( m_parent->getDelayTime()*0.15f ); arrowAnimation->setStartValue( 0.0 ); arrowAnimation->setEndValue( 1.0 ); m_arrowAnimation = arrowAnimation; } m_fadeInArrow = true; arrowAnimation->setEasingCurve( QEasingCurve::InQuad ); arrowAnimation->setDirection( QAbstractAnimation::Forward ); arrowAnimation->start(); } void DesktopChangeItem::hideArrow() { m_fadeInArrow = false; QPropertyAnimation *arrowAnimation = m_arrowAnimation.data(); if( arrowAnimation ) { arrowAnimation->setEasingCurve( QEasingCurve::OutQuad ); arrowAnimation->setDirection( QAbstractAnimation::Backward ); arrowAnimation->start( QAbstractAnimation::DeleteWhenStopped ); connect( arrowAnimation, SIGNAL(finished()), this, SLOT(arrowAnimationFinished()) ); } } void DesktopChangeItem::startDesktopHighLightAnimation( int time ) { QPropertyAnimation *highLightAnimation = m_highLightAnimation.data(); if( !highLightAnimation ) { highLightAnimation = new QPropertyAnimation( this, "highLightValue" ); highLightAnimation->setDuration( time ); highLightAnimation->setStartValue( 0.0 ); highLightAnimation->setEndValue( 1.0 ); m_highLightAnimation = highLightAnimation; } m_fadeInHighLight = true; highLightAnimation->setEasingCurve( QEasingCurve::InQuad ); highLightAnimation->setDirection( QAbstractAnimation::Forward ); highLightAnimation->start(); } void DesktopChangeItem::stopDesktopHighLightAnimation() { m_fadeInHighLight = false; QPropertyAnimation *highLightAnimation = m_highLightAnimation.data(); if(highLightAnimation) { highLightAnimation->setEasingCurve( QEasingCurve::OutQuad ); highLightAnimation->setDirection( QAbstractAnimation::Backward ); highLightAnimation->start( QAbstractAnimation::DeleteWhenStopped ); } } void DesktopChangeItem::arrowAnimationFinished() { if( !m_fadeInArrow ) m_arrowShown = false; } void DesktopChangeItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* , QWidget* ) { if( m_wspace->currentDesktop() == m_desktop || (!m_highLightAnimation.isNull() && m_highLightAnimation.data()->state() == QAbstractAnimation::Running) ) { qreal left, top, right, bottom; m_parent->itemFrame()->getMargins( left, top, right, bottom ); if( !m_highLightAnimation.isNull() && m_highLightAnimation.data()->state() == QAbstractAnimation::Running ) { // there is an animation - so we use transition from normal to active or vice versa if( m_fadeInHighLight ) { m_parent->itemFrame()->setElementPrefix( "normal" ); QPixmap normal = m_parent->itemFrame()->framePixmap(); m_parent->itemFrame()->setElementPrefix( "hover" ); QPixmap result = Plasma::PaintUtils::transition( normal, m_parent->itemFrame()->framePixmap(), m_highLightValue ); painter->drawPixmap( boundingRect().toRect(), result); } else { m_parent->itemFrame()->setElementPrefix( "hover" ); QPixmap normal = m_parent->itemFrame()->framePixmap(); m_parent->itemFrame()->setElementPrefix( "normal" ); QPixmap result = Plasma::PaintUtils::transition( normal, m_parent->itemFrame()->framePixmap(), 1.0 - m_highLightValue ); painter->drawPixmap( boundingRect().toRect(), result); } } else { // no animation - just render the active frame m_parent->itemFrame()->setElementPrefix( "hover" ); m_parent->itemFrame()->paintFrame( painter, boundingRect() ); } QColor rectColor = Plasma::Theme::defaultTheme()->color( Plasma::Theme::TextColor ); rectColor.setAlphaF( 0.6 * m_highLightValue ); QBrush rectBrush = QBrush( rectColor ); painter->fillRect( boundingRect().adjusted( left, top, -right, -bottom ), rectBrush ); } else { m_parent->itemFrame()->setElementPrefix( "normal" ); m_parent->itemFrame()->paintFrame( painter, boundingRect() ); } if( !m_arrowShown ) return; // paint the arrow QPixmap icon; int iconWidth = 32; qreal maxsize = qMin( boundingRect().width(), boundingRect().height() ); if( maxsize > 128.0 ) iconWidth = 128; else if( maxsize > 64.0 ) iconWidth = 64.0; else if( maxsize > 32.0 ) iconWidth = 32.0; else iconWidth = 16.0; QRect iconRect = QRect( boundingRect().x() + boundingRect().width()/2 - iconWidth/2, boundingRect().y() + boundingRect().height()/2 - iconWidth/2, iconWidth, iconWidth ); switch( m_arrow ) { case UP: icon = KIconLoader::global()->loadIcon( "go-up", KIconLoader::Desktop, iconWidth ); break; case DOWN: icon = KIconLoader::global()->loadIcon( "go-down", KIconLoader::Desktop, iconWidth ); break; case LEFT: icon = KIconLoader::global()->loadIcon( "go-previous", KIconLoader::Desktop, iconWidth ); break; case RIGHT: icon = KIconLoader::global()->loadIcon( "go-next", KIconLoader::Desktop, iconWidth ); break; default: break; } if( m_arrow != NONE ) { if( !m_arrowAnimation.isNull() && m_arrowAnimation.data()->state() == QAbstractAnimation::Running && !qFuzzyCompare(m_arrowValue, qreal(1.0)) ) { QPixmap temp( icon.size() ); temp.fill( Qt::transparent ); QPainter p( &temp ); p.setCompositionMode( QPainter::CompositionMode_Source ); p.drawPixmap( 0, 0, icon ); p.setCompositionMode( QPainter::CompositionMode_DestinationIn ); p.fillRect( temp.rect(), QColor( 0, 0, 0, 255*m_arrowValue ) ); p.end(); icon = temp; } painter->drawPixmap( iconRect, icon ); } } } #include "desktopchangeosd.moc"