1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
22
#include "desktopchangeosd.h"
23
#include <QTextStream>
24
#include "workspace.h"
26
#include <X11/extensions/shape.h>
29
#include <QEasingCurve>
30
#include <QPropertyAnimation>
32
#include <QGraphicsScene>
34
#include <KDE/Plasma/Theme>
35
#include <KDE/Plasma/PaintUtils>
36
#include <kiconloader.h>
41
DesktopChangeOSD::DesktopChangeOSD(Workspace* ws)
50
setWindowFlags(Qt::X11BypassWindowManagerHint);
51
setFrameStyle(QFrame::NoFrame);
52
viewport()->setAutoFillBackground(false);
53
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
54
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
55
setAttribute(Qt::WA_TranslucentBackground);
56
m_frame.setImagePath("dialogs/background");
57
m_frame.setCacheAllRenderedFrames(true);
58
m_frame.setEnabledBorders(Plasma::FrameSvg::AllBorders);
60
m_item_frame.setImagePath("widgets/pager");
61
m_item_frame.setCacheAllRenderedFrames(true);
62
m_item_frame.setEnabledBorders(Plasma::FrameSvg::AllBorders);
64
m_delayedHideTimer.setSingleShot(true);
65
connect(&m_delayedHideTimer, SIGNAL(timeout()), this, SLOT(hide()));
67
m_scene = new QGraphicsScene(0);
72
m_scene->addItem(new DesktopChangeText(m_wspace));
75
DesktopChangeOSD::~DesktopChangeOSD()
80
void DesktopChangeOSD::reconfigure()
82
KSharedConfigPtr c(KGlobal::config());
83
const KConfigGroup cg = c->group("PopupInfo");
84
m_show = cg.readEntry("ShowPopup", false);
85
m_delayTime = cg.readEntry("PopupHideDelay", 1000);
86
m_textOnly = cg.readEntry("TextOnly", false);
87
numberDesktopsChanged();
90
void DesktopChangeOSD::desktopChanged(int old)
94
// we have to stop in case the old desktop does not exist anymore
95
if (old > m_wspace->numberOfDesktops())
97
// calculate where icons have to be shown
98
QPoint diff = m_wspace->desktopGridCoords(m_wspace->currentDesktop()) - m_wspace->desktopGridCoords(old);
99
QHash< int, DesktopChangeItem::Arrow > hash = QHash< int, DesktopChangeItem::Arrow>();
101
int target = m_wspace->currentDesktop();
105
// first go in x direction, then in y
106
while (desktop != target) {
110
hash.insert(desktop, DesktopChangeItem::LEFT);
111
desktop = m_wspace->desktopToLeft(desktop);
114
hash.insert(desktop, DesktopChangeItem::RIGHT);
115
desktop = m_wspace->desktopToRight(desktop);
120
hash.insert(desktop, DesktopChangeItem::DOWN);
121
desktop = m_wspace->desktopBelow(desktop);
124
// first go in y direction, then in x
125
while (target != desktop) {
129
hash.insert(desktop, DesktopChangeItem::UP);
130
desktop = m_wspace->desktopAbove(desktop);
136
hash.insert(desktop, DesktopChangeItem::LEFT);
137
desktop = m_wspace->desktopToLeft(desktop);
140
hash.insert(desktop, DesktopChangeItem::RIGHT);
141
desktop = m_wspace->desktopToRight(desktop);
146
// now we know which desktop has to show an arrow -> set the arrow for each desktop
147
int numberOfArrows = qAbs(diff.x()) + qAbs(diff.y());
148
foreach (QGraphicsItem * it, m_scene->items()) {
149
DesktopChangeItem* item = qgraphicsitem_cast< DesktopChangeItem* >(it);
151
if (hash.contains(item->desktop())) {
152
QPoint distance = m_wspace->desktopGridCoords(m_wspace->currentDesktop())
153
- m_wspace->desktopGridCoords(item->desktop());
154
int desktopDistance = numberOfArrows - (qAbs(distance.x()) + qAbs(distance.y()));
155
int start = m_delayTime / numberOfArrows * desktopDistance - m_delayTime * 0.15f;
156
int stop = m_delayTime / numberOfArrows * (desktopDistance + 1) + m_delayTime * 0.15f;
157
start = qMax(start, 0);
158
item->setArrow(hash[ item->desktop()], start, stop);
160
item->setArrow(DesktopChangeItem::NONE, 0, 0);
162
if (old != m_wspace->currentDesktop()) {
163
if (item->desktop() == m_wspace->currentDesktop())
164
item->startDesktopHighLightAnimation(m_delayTime * 0.33);
165
if (m_active && item->desktop() == old)
166
item->stopDesktopHighLightAnimation();
171
// for text only we need to resize
174
// already active - just update and reset timer
182
// Set a zero inputmask, effectively making clicks go "through" the popup
183
// For those who impatiently wait to click on a dialog behind the it
184
XShapeCombineRectangles(display(), winId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
185
m_delayedHideTimer.start(m_delayTime);
188
void DesktopChangeOSD::hideEvent(QHideEvent*)
190
m_delayedHideTimer.stop();
194
void DesktopChangeOSD::drawBackground(QPainter* painter, const QRectF& rect)
197
painter->setCompositionMode(QPainter::CompositionMode_Source);
198
qreal left, top, right, bottom;
199
m_frame.getMargins(left, top, right, bottom);
200
m_frame.paintFrame(painter, rect.adjusted(-left, -top, right, bottom));
204
void DesktopChangeOSD::numberDesktopsChanged()
206
foreach (QGraphicsItem * it, m_scene->items()) {
207
DesktopChangeItem* item = qgraphicsitem_cast<DesktopChangeItem*>(it);
209
m_scene->removeItem(item);
214
for (int i = 1; i <= m_wspace->numberOfDesktops(); i++) {
215
DesktopChangeItem* item = new DesktopChangeItem(m_wspace, this, i);
216
m_scene->addItem(item);
221
void DesktopChangeOSD::resize()
223
QRect screenRect = m_wspace->clientArea(ScreenArea, m_wspace->activeScreen(), m_wspace->currentDesktop());
224
QRect fullRect = m_wspace->clientArea(FullArea, m_wspace->activeScreen(), m_wspace->currentDesktop());
225
qreal left, top, right, bottom;
226
m_frame.getMargins(left, top, right, bottom);
228
QSize desktopGridSize = m_wspace->desktopGridSize();
229
float itemWidth = fullRect.width() * 0.1f;
230
float itemHeight = fullRect.height() * 0.1f;
231
// 2 px distance between each desktop + each desktop a width of 5 % of full screen + borders
232
float width = (desktopGridSize.width() - 1) * 2 + desktopGridSize.width() * itemWidth + left + right;
233
float height = (desktopGridSize.height() - 1) * 2 + top + bottom;
235
// bound width between ten and 33 percent of active screen
236
float tempWidth = qBound(screenRect.width() * 0.25f, width, screenRect.width() * 0.5f);
237
if (tempWidth != width) {
238
// have to adjust the height
240
itemWidth = (width - (desktopGridSize.width() - 1) * 2 - left - right) / desktopGridSize.width();
241
itemHeight = itemWidth * (float)((float)fullRect.height() / (float)fullRect.width());
243
height += itemHeight * desktopGridSize.height();
244
height += fontMetrics().height() + 4;
246
// we do not increase height, but it's bound to a third of screen height
247
float tempHeight = qMin(height, screenRect.height() * 0.5f);
248
float itemOffset = 0.0f;
249
if (tempHeight != height) {
250
// have to adjust item width
252
itemHeight = (height - (fontMetrics().height() + 4) - top - bottom - (desktopGridSize.height() - 1) * 2) /
253
desktopGridSize.height();
254
itemOffset = itemWidth;
255
itemWidth = itemHeight * (float)((float)fullRect.width() / (float)fullRect.height());
256
itemOffset -= itemWidth;
257
itemOffset *= (float)desktopGridSize.width() * 0.5f;
260
// set size to the desktop name if the "pager" is not shown
262
height = fontMetrics().height() + 4 + top + bottom;
263
width = fontMetrics().boundingRect(m_wspace->desktopName(m_wspace->currentDesktop())).width() +
267
QRect rect = QRect(screenRect.x() + (screenRect.width() - width) / 2,
268
screenRect.y() + (screenRect.height() - height) / 2,
272
m_scene->setSceneRect(0, 0, width, height);
273
m_frame.resizeFrame(QSize(width, height));
275
if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) {
277
Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame.mask());
278
Plasma::WindowEffects::overrideShadow(winId(), true);
280
// do not trim to mask with compositing enabled, otherwise shadows are cropped
281
setMask(m_frame.mask());
285
m_item_frame.setElementPrefix("normal");
286
m_item_frame.resizeFrame(QSize(itemWidth, itemHeight));
287
m_item_frame.setElementPrefix("hover");
288
m_item_frame.resizeFrame(QSize(itemWidth, itemHeight));
291
foreach (QGraphicsItem * it, m_scene->items()) {
292
DesktopChangeItem* item = qgraphicsitem_cast<DesktopChangeItem*>(it);
294
item->setWidth(itemWidth);
295
item->setHeight(itemHeight);
296
QPoint coords = m_wspace->desktopGridCoords(item->desktop());
297
item->setPos(left + itemOffset + coords.x()*(itemWidth + 2),
298
top + fontMetrics().height() + 4 + coords.y()*(itemHeight + 4));
300
DesktopChangeText* text = qgraphicsitem_cast<DesktopChangeText*>(it);
302
text->setPos(left, top);
303
text->setWidth(width - left - right);
305
text->setHeight(fontMetrics().height() + 4);
307
text->setHeight(fontMetrics().height());
312
//*******************************
314
//*******************************
315
DesktopChangeText::DesktopChangeText(Workspace* ws)
323
DesktopChangeText::~DesktopChangeText()
327
QRectF DesktopChangeText::boundingRect() const
329
return QRectF(0, 0, m_width, m_height);
332
void DesktopChangeText::paint(QPainter* painter, const QStyleOptionGraphicsItem* , QWidget*)
334
painter->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
335
painter->drawText(boundingRect(), Qt::AlignCenter | Qt:: AlignVCenter,
336
m_wspace->desktopName(m_wspace->currentDesktop()));
339
//*******************************
341
//*******************************
342
DesktopChangeItem::DesktopChangeItem(Workspace* ws, DesktopChangeOSD* parent, int desktop)
350
, m_arrowShown(false)
351
, m_fadeInArrow(false)
352
, m_fadeInHighLight(false)
354
, m_highLightValue(0.0)
356
m_delayed_show_arrow_timer.setSingleShot(true);
357
m_delayed_hide_arrow_timer.setSingleShot(true);
358
connect(&m_delayed_show_arrow_timer, SIGNAL(timeout()), this, SLOT(showArrow()));
359
connect(&m_delayed_hide_arrow_timer, SIGNAL(timeout()), this, SLOT(hideArrow()));
362
DesktopChangeItem::~DesktopChangeItem()
366
QRectF DesktopChangeItem::boundingRect() const
368
return QRectF(0, 0, m_width, m_height);
371
void DesktopChangeItem::setArrow(Arrow arrow, int start_delay, int hide_delay)
374
m_delayed_show_arrow_timer.stop();
375
m_delayed_hide_arrow_timer.stop();
377
QPropertyAnimation *arrowAnimation = m_arrowAnimation.data();
378
if (arrowAnimation) {
379
arrowAnimation->stop();
380
m_arrowAnimation.clear();
383
m_arrowShown = false;
385
if (m_arrow != NONE) {
386
m_delayed_show_arrow_timer.start(start_delay);
387
m_delayed_hide_arrow_timer.start(hide_delay);
391
qreal DesktopChangeItem::arrowValue() const
393
qCritical() << __func__ << m_arrowValue;
397
qreal DesktopChangeItem::highLightValue() const
399
return m_highLightValue;
402
void DesktopChangeItem::setArrowValue(qreal value)
404
m_arrowValue = value;
409
void DesktopChangeItem::setHighLightValue(qreal value)
411
m_highLightValue = value;
416
void DesktopChangeItem::showArrow()
420
QPropertyAnimation *arrowAnimation = m_arrowAnimation.data();
421
if (!arrowAnimation) {
422
arrowAnimation = new QPropertyAnimation(this, "arrowValue");
423
arrowAnimation->setDuration(m_parent->getDelayTime() * 0.15f);
424
arrowAnimation->setStartValue(0.0);
425
arrowAnimation->setEndValue(1.0);
427
m_arrowAnimation = arrowAnimation;
430
m_fadeInArrow = true;
432
arrowAnimation->setEasingCurve(QEasingCurve::InQuad);
433
arrowAnimation->setDirection(QAbstractAnimation::Forward);
434
arrowAnimation->start();
437
void DesktopChangeItem::hideArrow()
439
m_fadeInArrow = false;
441
QPropertyAnimation *arrowAnimation = m_arrowAnimation.data();
442
if (arrowAnimation) {
443
arrowAnimation->setEasingCurve(QEasingCurve::OutQuad);
444
arrowAnimation->setDirection(QAbstractAnimation::Backward);
445
arrowAnimation->start(QAbstractAnimation::DeleteWhenStopped);
447
connect(arrowAnimation, SIGNAL(finished()), this, SLOT(arrowAnimationFinished()));
451
void DesktopChangeItem::startDesktopHighLightAnimation(int time)
453
QPropertyAnimation *highLightAnimation = m_highLightAnimation.data();
454
if (!highLightAnimation) {
455
highLightAnimation = new QPropertyAnimation(this, "highLightValue");
456
highLightAnimation->setDuration(time);
457
highLightAnimation->setStartValue(0.0);
458
highLightAnimation->setEndValue(1.0);
460
m_highLightAnimation = highLightAnimation;
463
m_fadeInHighLight = true;
465
highLightAnimation->setEasingCurve(QEasingCurve::InQuad);
466
highLightAnimation->setDirection(QAbstractAnimation::Forward);
467
highLightAnimation->start();
470
void DesktopChangeItem::stopDesktopHighLightAnimation()
472
m_fadeInHighLight = false;
474
QPropertyAnimation *highLightAnimation = m_highLightAnimation.data();
475
if (highLightAnimation) {
476
highLightAnimation->setEasingCurve(QEasingCurve::OutQuad);
477
highLightAnimation->setDirection(QAbstractAnimation::Backward);
478
highLightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
482
void DesktopChangeItem::arrowAnimationFinished()
485
m_arrowShown = false;
488
void DesktopChangeItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* , QWidget*)
490
if (m_wspace->currentDesktop() == m_desktop || (!m_highLightAnimation.isNull() &&
491
m_highLightAnimation.data()->state() == QAbstractAnimation::Running)) {
492
qreal left, top, right, bottom;
493
m_parent->itemFrame()->getMargins(left, top, right, bottom);
494
if (!m_highLightAnimation.isNull() &&
495
m_highLightAnimation.data()->state() == QAbstractAnimation::Running) {
496
// there is an animation - so we use transition from normal to active or vice versa
497
if (m_fadeInHighLight) {
498
m_parent->itemFrame()->setElementPrefix("normal");
499
QPixmap normal = m_parent->itemFrame()->framePixmap();
500
m_parent->itemFrame()->setElementPrefix("hover");
501
QPixmap result = Plasma::PaintUtils::transition(normal,
502
m_parent->itemFrame()->framePixmap(), m_highLightValue);
503
painter->drawPixmap(boundingRect().toRect(), result);
505
m_parent->itemFrame()->setElementPrefix("hover");
506
QPixmap normal = m_parent->itemFrame()->framePixmap();
507
m_parent->itemFrame()->setElementPrefix("normal");
508
QPixmap result = Plasma::PaintUtils::transition(normal,
509
m_parent->itemFrame()->framePixmap(), 1.0 - m_highLightValue);
510
painter->drawPixmap(boundingRect().toRect(), result);
513
// no animation - just render the active frame
514
m_parent->itemFrame()->setElementPrefix("hover");
515
m_parent->itemFrame()->paintFrame(painter, boundingRect());
517
QColor rectColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
518
rectColor.setAlphaF(0.6 * m_highLightValue);
519
QBrush rectBrush = QBrush(rectColor);
520
painter->fillRect(boundingRect().adjusted(left, top, -right, -bottom), rectBrush);
522
m_parent->itemFrame()->setElementPrefix("normal");
523
m_parent->itemFrame()->paintFrame(painter, boundingRect());
531
qreal maxsize = qMin(boundingRect().width(), boundingRect().height());
534
else if (maxsize > 64.0)
536
else if (maxsize > 32.0)
540
QRect iconRect = QRect(boundingRect().x() + boundingRect().width() / 2 - iconWidth / 2,
541
boundingRect().y() + boundingRect().height() / 2 - iconWidth / 2,
542
iconWidth, iconWidth);
545
icon = KIconLoader::global()->loadIcon("go-up", KIconLoader::Desktop, iconWidth);
548
icon = KIconLoader::global()->loadIcon("go-down", KIconLoader::Desktop, iconWidth);
551
icon = KIconLoader::global()->loadIcon("go-previous", KIconLoader::Desktop, iconWidth);
554
icon = KIconLoader::global()->loadIcon("go-next", KIconLoader::Desktop, iconWidth);
559
if (m_arrow != NONE) {
560
if (!m_arrowAnimation.isNull() &&
561
m_arrowAnimation.data()->state() == QAbstractAnimation::Running &&
562
!qFuzzyCompare(m_arrowValue, qreal(1.0))) {
563
QPixmap temp(icon.size());
564
temp.fill(Qt::transparent);
567
p.setCompositionMode(QPainter::CompositionMode_Source);
568
p.drawPixmap(0, 0, icon);
569
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
570
p.fillRect(temp.rect(), QColor(0, 0, 0, 255 * m_arrowValue));
575
painter->drawPixmap(iconRect, icon);
580
#include "desktopchangeosd.moc"