/******************************************************************** 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 #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())); connect(ws, SIGNAL(currentDesktopChanged(int)), this, SLOT(desktopChanged(int))); connect(ws, SIGNAL(numberDesktopsChanged(int)), this, SLOT(numberDesktopsChanged())); connect(ws, SIGNAL(configChanged()), this, SLOT(reconfigure())); 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) { // Not for the very first time, only if something changed and there are more than 1 desktops if (old == 0 || old == m_wspace->currentDesktop() || m_wspace->numberOfDesktops() <= 1) return; 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"