kwin/desktopchangeosd.cpp
Arthur Arlt c1c883a8a7 Introduce new signal configChanged() to Workspace
To get rid of direct method calls and numerous #ifdefs a new signal
configChanged() is introduced to class Workspace. Every module
which is interested in reloading the configuration is needed to connect to
this signal.
Initially this is done for the reconfiguration of DesktopChangeOSD().
2011-07-15 22:05:38 +02:00

586 lines
20 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "desktopchangeosd.h"
#include <QTextStream>
#include "workspace.h"
#include <X11/extensions/shape.h>
#include <QDebug>
#include <QEasingCurve>
#include <QPropertyAnimation>
#include <QHash>
#include <QGraphicsScene>
#include <QRect>
#include <KDE/Plasma/Theme>
#include <KDE/Plasma/PaintUtils>
#include <kiconloader.h>
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<DesktopChangeItem*>(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<DesktopChangeItem*>(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<DesktopChangeText*>(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"