kwin/effects/boxswitch/boxswitch.cpp
Martin Gräßlin fe4329a252 Only call active effects in the effect chain
Each effect is able to declare itself as currently being active,
that is transforming windows or painting or screen or doing anything
during the current rendered frame.

This change eliminates the hottest path inside KWin identified by
callgrind.

REVIEW: 102449
2011-08-29 07:06:58 +02:00

965 lines
38 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Philip Falkner <philip.falkner@gmail.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 "boxswitch.h"
#include <kwinconfig.h>
#include <QMouseEvent>
#include <QSize>
#include <kapplication.h>
#include <kcolorscheme.h>
#include <kconfiggroup.h>
namespace KWin
{
KWIN_EFFECT(boxswitch, BoxSwitchEffect)
BoxSwitchEffect::BoxSwitchEffect()
: mActivated(0)
, mMode(0)
, thumbnailFrame(effects->effectFrame(EffectFrameStyled))
, selected_window(0)
, painting_desktop(0)
, animation(false)
, highlight_is_set(false)
, primaryTabBox(true)
, secondaryTabBox(false)
, mProxy(this)
, mProxyActivated(0)
, mProxyAnimateSwitch(0)
, mProxyShowText(0)
, mPositioningFactor(0.5f)
{
text_font.setBold(true);
text_font.setPointSize(12);
thumbnailFrame->setFont(text_font);
thumbnailFrame->setAlignment(Qt::AlignBottom | Qt::AlignHCenter);
highlight_margin = 10;
reconfigure(ReconfigureAll);
connect(effects, SIGNAL(windowClosed(EffectWindow*)), this, SLOT(slotWindowClosed(EffectWindow*)));
connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int)));
connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed()));
connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated()));
connect(effects, SIGNAL(windowGeometryShapeChanged(EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(EffectWindow*,QRect)));
connect(effects, SIGNAL(windowDamaged(EffectWindow*,QRect)), this, SLOT(slotWindowDamaged(EffectWindow*,QRect)));
}
BoxSwitchEffect::~BoxSwitchEffect()
{
delete thumbnailFrame;
}
void BoxSwitchEffect::reconfigure(ReconfigureFlags)
{
color_frame = KColorScheme(QPalette::Active, KColorScheme::Window).background().color();
color_frame.setAlphaF(0.9);
color_highlight = KColorScheme(QPalette::Active, KColorScheme::Selection).background().color();
color_highlight.setAlphaF(0.9);
activeTimeLine.setDuration(animationTime(250));
activeTimeLine.setCurveShape(QTimeLine::EaseInOutCurve);
timeLine.setDuration(animationTime(150));
timeLine.setCurveShape(QTimeLine::EaseInOutCurve);
KConfigGroup conf = effects->effectConfig("BoxSwitch");
bg_opacity = conf.readEntry("BackgroundOpacity", 25) / 100.0;
elevate_window = conf.readEntry("ElevateSelected", true);
mAnimateSwitch = conf.readEntry("AnimateSwitch", false);
primaryTabBox = conf.readEntry("TabBox", true);
secondaryTabBox = conf.readEntry("TabBoxAlternative", false);
}
void BoxSwitchEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
{
if (activeTimeLine.currentValue() != 0.0 && !mProxyActivated) {
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
if (windows.contains(w)) {
if (w == selected_window)
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
else
data.setTranslucent();
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE | EffectWindow::PAINT_DISABLED_BY_DESKTOP);
}
} else {
if (painting_desktop) {
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 BoxSwitchEffect::prePaintScreen(ScreenPrePaintData& data, int time)
{
if (mActivated)
activeTimeLine.setCurrentTime(activeTimeLine.currentTime() + time);
else {
activeTimeLine.setCurrentTime(activeTimeLine.currentTime() - time);
if (activeTimeLine.currentValue() == 0.0) {
// No longer need the window data
qDeleteAll(windows);
windows.clear();
}
}
if (mActivated && animation) {
timeLine.setCurrentTime(timeLine.currentTime() + time);
calculateItemSizes();
}
effects->prePaintScreen(data, time);
}
void BoxSwitchEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
{
effects->paintScreen(mask, region, data);
if (mActivated && !mProxyActivated) {
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
paintWindowsBox(region);
} else {
if (!painting_desktop) {
thumbnailFrame->setSelection(desktops[ selected_desktop ]->area);
thumbnailFrame->render(region);
QHash< int, ItemInfo* >::const_iterator i;
for (i = desktops.constBegin(); i != desktops.constEnd(); ++i) {
painting_desktop = i.key();
paintDesktopThumbnail(painting_desktop);
}
painting_desktop = 0;
}
}
}
}
void BoxSwitchEffect::paintWindowsBox(const QRegion& region)
{
if ((mAnimateSwitch && !mProxyActivated) || (mProxyActivated && mProxyAnimateSwitch))
thumbnailFrame->setSelection(highlight_area);
else {
ItemInfo *info = windows.value(selected_window, 0);
thumbnailFrame->setSelection(info ? info->area : QRect());
}
thumbnailFrame->render(region);
if ((mAnimateSwitch && !mProxyActivated) || (mProxyActivated && mProxyAnimateSwitch)) {
// HACK: PaintClipper is used because window split is somehow wrong if the height is greater than width
PaintClipper::push(frame_area);
QHash< EffectWindow*, ItemInfo* >::const_iterator i;
for (i = windows.constBegin(); i != windows.constEnd(); ++i) {
paintWindowThumbnail(i.key());
paintWindowIcon(i.key());
}
PaintClipper::pop(frame_area);
} else {
QHash< EffectWindow*, ItemInfo* >::const_iterator i;
for (i = windows.constBegin(); i != windows.constEnd(); ++i) {
paintWindowThumbnail(i.key());
paintWindowIcon(i.key());
}
}
}
void BoxSwitchEffect::postPaintScreen()
{
if (mActivated && activeTimeLine.currentValue() != 1.0)
effects->addRepaintFull();
if (!mActivated && activeTimeLine.currentValue() != 0.0)
effects->addRepaintFull();
if (mActivated && animation) {
if (timeLine.currentValue() == 1.0) {
timeLine.setCurrentTime(0);
animation = false;
if (!scheduled_directions.isEmpty()) {
direction = scheduled_directions.dequeue();
animation = true;
}
}
QRect repaint = QRect(frame_area.x() - item_max_size.width() * 0.5,
frame_area.y(),
frame_area.width() + item_max_size.width(),
frame_area.height());
effects->addRepaint(repaint);
}
effects->postPaintScreen();
}
void BoxSwitchEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
{
if (((mActivated && (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode))
|| (!mActivated && activeTimeLine.currentValue() != 0.0)) && !mProxyActivated) {
if (windows.contains(w) && w != selected_window) {
if (w->isMinimized() || !w->isOnCurrentDesktop())
// TODO: When deactivating minimized windows are not painted at all
data.opacity *= activeTimeLine.currentValue() * bg_opacity;
else
data.opacity *= 1.0 - activeTimeLine.currentValue() * (1.0 - bg_opacity);
}
}
effects->paintWindow(w, mask, region, data);
}
void BoxSwitchEffect::windowInputMouseEvent(Window w, QEvent* e)
{
assert(w == mInput);
Q_UNUSED(w);
if (e->type() != QEvent::MouseButtonPress)
return;
QPoint pos = static_cast< QMouseEvent* >(e)->pos();
pos += frame_area.topLeft();
// determine which item was clicked
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
QHash< EffectWindow*, ItemInfo* >::const_iterator i;
for (i = windows.constBegin(); i != windows.constEnd(); ++i) {
if (i.value()->clickable.contains(pos)) {
effects->setTabBoxWindow(i.key());
break;
}
}
// special handling for second half of window in case of animation and even number of windows
if (mAnimateSwitch && (windows.size() % 2 == 0)) {
QRect additionalRect = QRect(frame_area.x(), frame_area.y(),
item_max_size.width() * 0.5, item_max_size.height());
if (additionalRect.contains(pos)) {
effects->setTabBoxWindow(right_window);
}
}
} else {
QHash< int, ItemInfo* >::const_iterator i;
for (i = desktops.constBegin(); i != desktops.constEnd(); ++i) {
if (i.value()->clickable.contains(pos))
effects->setTabBoxDesktop(i.key());
}
}
}
void BoxSwitchEffect::slotWindowDamaged(EffectWindow* w, const QRect& damage)
{
Q_UNUSED(damage);
if (mActivated) {
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
if (windows.contains(w)) {
effects->addRepaint(frame_area);
}
} else {
if (w->isOnAllDesktops()) {
foreach (ItemInfo * info, desktops)
effects->addRepaint(info->area);
} else {
effects->addRepaint(desktops[ w->desktop()]->area);
}
}
#ifdef KWIN_HAVE_OPENGLES
// without full repaints, blur effect flickers on GLES, see BUG 272688
effects->addRepaintFull();
#endif
}
}
void BoxSwitchEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old)
{
if (mActivated) {
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
ItemInfo *info = windows.value( w, 0L );
if (info && w->size() != old.size()) {
effects->addRepaint(info->area);
}
} else {
if (w->isOnAllDesktops()) {
foreach (ItemInfo * info, desktops)
effects->addRepaint(info->area);
} else {
effects->addRepaint(desktops[ w->desktop()]->area);
}
}
}
}
void BoxSwitchEffect::slotTabBoxAdded(int mode)
{
if (!mActivated) {
if ((mode == TabBoxWindowsMode && primaryTabBox) ||
(mode == TabBoxWindowsAlternativeMode && secondaryTabBox)) {
if (effects->currentTabBoxWindowList().count() > 0) {
mMode = mode;
effects->refTabBox();
highlight_is_set = false;
animation = false;
scheduled_directions.clear();
right_window = 0;
setActive();
}
} else if (mode == TabBoxDesktopListMode || mode == TabBoxDesktopMode) {
// DesktopMode
if (effects->currentTabBoxDesktopList().count() > 0) {
mMode = mode;
painting_desktop = 0;
effects->refTabBox();
setActive();
}
}
}
}
void BoxSwitchEffect::slotTabBoxClosed()
{
if (mActivated)
setInactive();
}
void BoxSwitchEffect::slotTabBoxUpdated()
{
if (mActivated) {
if ((mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode)
&& selected_window != effects->currentTabBoxWindow()) {
if (selected_window != NULL) {
if ((mAnimateSwitch && !mProxyActivated) || (mProxyActivated && mProxyAnimateSwitch)) {
int old_index = effects->currentTabBoxWindowList().indexOf(selected_window);
int new_index = effects->currentTabBoxWindowList().indexOf(effects->currentTabBoxWindow());
Direction new_direction;
int distance = new_index - old_index;
if (distance > 0)
new_direction = Left;
if (distance < 0)
new_direction = Right;
if (distance != 0) {
distance = abs(distance);
int tempDistance = effects->currentTabBoxWindowList().count() - distance;
if (tempDistance < abs(distance)) {
distance = tempDistance;
if (new_direction == Left)
new_direction = Right;
else
new_direction = Left;
}
if (!animation) {
animation = true;
direction = new_direction;
distance--;
}
for (int i = 0; i < distance; i++) {
if (!scheduled_directions.isEmpty() && scheduled_directions.last() != new_direction)
scheduled_directions.pop_back();
else
scheduled_directions.enqueue(new_direction);
if (scheduled_directions.count() == effects->currentTabBoxWindowList().count())
scheduled_directions.clear();
}
}
}
if (ItemInfo *info = windows.value(selected_window, 0))
effects->addRepaint(info->area);
selected_window->addRepaintFull();
}
setSelectedWindow(effects->currentTabBoxWindow());
if (ItemInfo *info = windows.value(selected_window, 0))
effects->addRepaint(info->area);
if (selected_window) // @Martin can effects->currentTabBoxWindow() be NULL?
selected_window->addRepaintFull();
effects->addRepaint(text_area);
} else if (mMode != TabBoxWindowsMode && mMode != TabBoxWindowsAlternativeMode) {
// DesktopMode
if (ItemInfo *info = desktops.value(selected_desktop, 0))
effects->addRepaint(info->area);
selected_desktop = effects->currentTabBoxDesktop();
if (!mProxyActivated || mProxyShowText)
thumbnailFrame->setText(effects->desktopName(selected_desktop));
if (ItemInfo *info = desktops.value(selected_desktop, 0))
effects->addRepaint(info->area);
effects->addRepaint(text_area);
if (effects->currentTabBoxDesktopList() == original_desktops)
return;
original_desktops = effects->currentTabBoxDesktopList();
}
// for safety copy list each time tabbox is updated
original_windows = effects->currentTabBoxWindowList();
effects->addRepaint(frame_area);
calculateFrameSize();
calculateItemSizes();
moveResizeInputWindow(frame_area.x(), frame_area.y(), frame_area.width(), frame_area.height());
effects->addRepaint(frame_area);
}
}
void BoxSwitchEffect::setActive()
{
mActivated = true;
// Just in case we are activated again before the deactivation animation finished
qDeleteAll(windows);
windows.clear();
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
original_windows = effects->currentTabBoxWindowList();
setSelectedWindow(effects->currentTabBoxWindow());
} else {
original_desktops = effects->currentTabBoxDesktopList();
selected_desktop = effects->currentTabBoxDesktop();
if (!mProxyActivated || mProxyShowText)
thumbnailFrame->setText(effects->desktopName(selected_desktop));
}
calculateFrameSize();
calculateItemSizes();
if (!mProxyActivated) {
// only create the input window when effect is not activated via the proxy
mInput = effects->createInputWindow(this, frame_area.x(), frame_area.y(),
frame_area.width(), frame_area.height(), Qt::ArrowCursor);
}
effects->addRepaint(frame_area);
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
QHash< EffectWindow*, ItemInfo* >::const_iterator i;
for (i = windows.constBegin(); i != windows.constEnd(); ++i) {
i.key()->addRepaintFull();
}
}
}
void BoxSwitchEffect::setInactive()
{
mActivated = false;
effects->unrefTabBox();
if (!mProxyActivated && mInput != None) {
effects->destroyInputWindow(mInput);
mInput = None;
}
mProxyActivated = false;
mPositioningFactor = 0.5f;
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
QHash< EffectWindow*, ItemInfo* >::const_iterator i;
for (i = windows.constBegin(); i != windows.constEnd(); ++i) {
if (i.key() != selected_window)
i.key()->addRepaintFull();
}
// We don't unset the selected window so we have correct fading
// But we do need to remove elevation status
if (elevate_window && selected_window)
effects->setElevatedWindow(selected_window, false);
foreach (EffectWindow * w, referrencedWindows) {
w->unrefWindow();
}
referrencedWindows.clear();
} else {
// DesktopMode
qDeleteAll(windows);
desktops.clear();
}
thumbnailFrame->free();
effects->addRepaint(frame_area);
frame_area = QRect();
}
void BoxSwitchEffect::setSelectedWindow(EffectWindow* w)
{
if (elevate_window && selected_window) {
effects->setElevatedWindow(selected_window, false);
}
selected_window = w;
if (selected_window && (!mProxyActivated || mProxyShowText))
thumbnailFrame->setText(selected_window->caption());
if (elevate_window && w) {
effects->setElevatedWindow(selected_window, true);
}
}
void BoxSwitchEffect::slotWindowClosed(EffectWindow* w)
{
if (w == selected_window) {
setSelectedWindow(0);
}
QHash<EffectWindow*, ItemInfo*>::iterator it = windows.find(w);
if (it != windows.end()) {
w->refWindow();
referrencedWindows.append(w);
original_windows.removeAll(w);
delete *it; *it = 0;
windows.erase( it );
effects->addRepaintFull();
}
}
void BoxSwitchEffect::moveResizeInputWindow(int x, int y, int width, int height)
{
XMoveWindow(display(), mInput, x, y);
XResizeWindow(display(), mInput, width, height);
}
void BoxSwitchEffect::calculateFrameSize()
{
int itemcount;
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
itemcount = original_windows.count();
item_max_size.setWidth(200);
item_max_size.setHeight(200);
} else {
itemcount = original_desktops.count();
item_max_size.setWidth(200);
item_max_size.setHeight(200);
}
// How much height to reserve for a one-line text label
text_area.setHeight(QFontMetrics(text_font).height() * 1.2);
// Separator space between items and text
const int separator_height = 6;
// Shrink the size until all windows/desktops can fit onscreen
frame_area.setWidth(itemcount * item_max_size.width());
QRect screenr = effects->clientArea(PlacementArea, effects->activeScreen(), effects->currentDesktop());
while (frame_area.width() > screenr.width()) {
item_max_size /= 2;
frame_area.setWidth(itemcount * item_max_size.width());
}
frame_area.setHeight(item_max_size.height() +
separator_height + text_area.height());
if (mProxyActivated && !mProxyShowText)
frame_area.setHeight(item_max_size.height());
text_area.setWidth(frame_area.width());
frame_area.moveTo(screenr.x() + (screenr.width() - frame_area.width()) / 2,
screenr.y() + (screenr.height() - frame_area.height()) / 2 * mPositioningFactor * 2);
text_area.moveTo(frame_area.x(),
frame_area.y() + item_max_size.height() + separator_height);
thumbnailFrame->setGeometry(frame_area);
}
void BoxSwitchEffect::calculateItemSizes()
{
if (mMode == TabBoxWindowsMode || mMode == TabBoxWindowsAlternativeMode) {
qDeleteAll(windows);
windows.clear();
if ((mAnimateSwitch && !mProxyActivated) || (mProxyActivated && mProxyAnimateSwitch)) {
if (original_windows.isEmpty()) {
// can happen when last window is closed.
return;
}
int index = original_windows.indexOf(effects->currentTabBoxWindow());
int leftIndex = index;
int rightIndex = index + 1;
if (rightIndex == original_windows.count())
rightIndex = 0;
EffectWindowList ordered_windows;
int leftWindowCount = original_windows.count() / 2;
int rightWindowCount = leftWindowCount;
if (original_windows.count() % 2 == 1)
leftWindowCount++;
for (int i = 0; i < leftWindowCount; i++) {
int tempIndex = (leftIndex - i);
if (tempIndex < 0)
tempIndex = original_windows.count() + tempIndex;
ordered_windows.prepend(original_windows[ tempIndex ]);
}
for (int i = 0; i < rightWindowCount; i++) {
int tempIndex = (rightIndex + i) % original_windows.count();
ordered_windows.append(original_windows[ tempIndex ]);
}
// move items cause of schedule
for (int i = 0; i < scheduled_directions.count(); i++) {
Direction actual = scheduled_directions[ i ];
if (actual == Left) {
EffectWindow* w = ordered_windows.takeLast();
ordered_windows.prepend(w);
} else {
EffectWindow* w = ordered_windows.takeFirst();
ordered_windows.append(w);
}
}
if (animation && timeLine.currentValue() < 0.5) {
if (direction == Left) {
EffectWindow* w = ordered_windows.takeLast();
edge_window = w;
ordered_windows.prepend(w);
} else {
EffectWindow* w = ordered_windows.takeFirst();
edge_window = w;
ordered_windows.append(w);
}
} else if (animation && timeLine.currentValue() >= 0.5) {
if (animation && direction == Left)
edge_window = ordered_windows.last();
if (animation && direction == Right)
edge_window = ordered_windows.first();
}
int offset = 0;
if (animation) {
if (direction == Left)
offset = (float)item_max_size.width() * (1.0 - timeLine.currentValue());
else
offset = -(float)item_max_size.width() * (1.0 - timeLine.currentValue());
}
for (int i = 0; i < ordered_windows.count(); i++) {
if (!ordered_windows.at(i))
continue;
EffectWindow* w = ordered_windows.at(i);
ItemInfo *info = windows.value(w, 0);
if (!info)
windows[ w ] = info = new ItemInfo();
info->iconFrame = effects->effectFrame(EffectFrameUnstyled, false);
info->iconFrame->setAlignment(Qt::AlignTop | Qt::AlignLeft);
info->iconFrame->setIcon(w->icon());
float moveIndex = i;
if (animation && timeLine.currentValue() < 0.5) {
if (direction == Left)
moveIndex--;
else
moveIndex++;
}
if (ordered_windows.count() % 2 == 0)
moveIndex += 0.5;
info->area = QRect(frame_area.x() + moveIndex * item_max_size.width() + offset,
frame_area.y(),
item_max_size.width(), item_max_size.height());
info->clickable = info->area;
}
if (ordered_windows.count() % 2 == 0) {
right_window = ordered_windows.last();
}
if (!highlight_is_set) {
ItemInfo *info = windows.value(selected_window, 0);
highlight_area = info ? info->area : QRect();
highlight_is_set = true;
}
} else {
for (int i = 0; i < original_windows.count(); i++) {
if (!original_windows.at(i))
continue;
EffectWindow* w = original_windows.at(i);
ItemInfo *info = windows.value(w, 0);
if (!info)
windows[ w ] = info = new ItemInfo();
info->iconFrame = effects->effectFrame(EffectFrameUnstyled, false);
info->iconFrame->setAlignment(Qt::AlignTop | Qt::AlignLeft);
info->iconFrame->setIcon(w->icon());
info->area = QRect(frame_area.x() + i * item_max_size.width(),
frame_area.y(),
item_max_size.width(), item_max_size.height());
info->clickable = info->area;
}
}
} else {
desktops.clear();
for (int i = 0; i < original_desktops.count(); i++) {
int it = original_desktops.at(i);
ItemInfo *info = desktops.value( it, 0 );
if (!info)
desktops[ it ] = info = new ItemInfo();
info->area = QRect(frame_area.x() + i * item_max_size.width(),
frame_area.y(),
item_max_size.width(), item_max_size.height());
info->clickable = info->area;
}
}
}
void BoxSwitchEffect::paintWindowThumbnail(EffectWindow* w)
{
ItemInfo *info = windows.value(w, 0);
if (!info)
return;
WindowPaintData data(w);
setPositionTransformations(data,
info->thumbnail, w,
info->area.adjusted(highlight_margin, highlight_margin, -highlight_margin, -highlight_margin),
Qt::KeepAspectRatio);
if (animation && (w == edge_window) && (windows.size() % 2 == 1)) {
// in case of animation:
// the window which has to change the side will be split and painted on both sides of the edge
double splitPoint = 0.0;
if (direction == Left) {
splitPoint = w->geometry().width() * timeLine.currentValue();
} else {
splitPoint = w->geometry().width() - w->geometry().width() * timeLine.currentValue();
}
data.quads = data.quads.splitAtX(splitPoint);
WindowQuadList left_quads;
WindowQuadList right_quads;
foreach (const WindowQuad & quad, data.quads) {
if (quad.left() >= splitPoint)
left_quads << quad;
if (quad.right() <= splitPoint)
right_quads << quad;
}
// the base position of the window is changed after half of the animation
if (timeLine.currentValue() < 0.5) {
if (direction == Left)
data.quads = left_quads;
else
data.quads = right_quads;
} else {
if (direction == Left)
data.quads = right_quads;
else
data.quads = left_quads;
}
// paint one part of the thumbnail
effects->paintWindow(w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
info->thumbnail, data);
QRect secondThumbnail;
// paint the second part of the thumbnail:
// the other window quads are needed and a new rect for transformation has to be calculated
if (direction == Left) {
if (timeLine.currentValue() < 0.5) {
data.quads = right_quads;
secondThumbnail = QRect(frame_area.x() + frame_area.width() -
(float)item_max_size.width() * timeLine.currentValue(),
frame_area.y(), item_max_size.width(), item_max_size.height());
} else {
data.quads = left_quads;
secondThumbnail = QRect(frame_area.x() - (float)item_max_size.width() * timeLine.currentValue(),
frame_area.y(), item_max_size.width(), item_max_size.height());
if (right_window)
secondThumbnail = QRect(frame_area.x() -
(float)item_max_size.width() * (timeLine.currentValue() - 0.5),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
} else {
if (timeLine.currentValue() < 0.5) {
data.quads = left_quads;
secondThumbnail = QRect(frame_area.x() -
(float)item_max_size.width() * (1.0 - timeLine.currentValue()),
frame_area.y(), item_max_size.width(), item_max_size.height());
} else {
data.quads = right_quads;
secondThumbnail = QRect(frame_area.x() + frame_area.width() -
(float)item_max_size.width() * (1.0 - timeLine.currentValue()),
frame_area.y(), item_max_size.width(), item_max_size.height());
}
}
setPositionTransformations(data,
info->thumbnail, w,
secondThumbnail.adjusted(highlight_margin, highlight_margin, -highlight_margin, -highlight_margin),
Qt::KeepAspectRatio);
effects->paintWindow(w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
info->thumbnail, data);
} else if ((windows.size() % 2 == 0) && (w == right_window)) {
// in case of even number of thumbnails:
// the window on the right is painted one half on left and on half on the right side
float animationOffset = 0.0f;
double splitPoint = w->geometry().width() * 0.5;
if (animation && timeLine.currentValue() <= 0.5) {
// in case of animation the right window has only to be split during first half of animation
if (direction == Left) {
splitPoint += w->geometry().width() * timeLine.currentValue();
animationOffset = -(float)item_max_size.width() * timeLine.currentValue();
} else {
splitPoint -= w->geometry().width() * timeLine.currentValue();
animationOffset = (float)item_max_size.width() * timeLine.currentValue();
}
}
if (animation && timeLine.currentValue() > 0.5) {
// at half of animation a different window has become the right window
// we have to adjust the splitting again
if (direction == Left) {
splitPoint -= w->geometry().width() * (1.0 - timeLine.currentValue());
animationOffset = (float)item_max_size.width() * (1.0 - timeLine.currentValue());
} else {
splitPoint += w->geometry().width() * (1.0 - timeLine.currentValue());
animationOffset = -(float)item_max_size.width() * (1.0 - timeLine.currentValue());
}
}
data.quads = data.quads.splitAtX(splitPoint);
WindowQuadList rightQuads;
WindowQuadList leftQuads;
foreach (const WindowQuad & quad, data.quads) {
if (quad.right() <= splitPoint)
leftQuads << quad;
else
rightQuads << quad;
}
// left quads are painted on right side of frame
data.quads = leftQuads;
effects->drawWindow(w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
info->thumbnail, data);
// right quads are painted on left side of frame
data.quads = rightQuads;
QRect secondThumbnail;
secondThumbnail = QRect(frame_area.x() -
(float)item_max_size.width() * 0.5 + animationOffset,
frame_area.y(), item_max_size.width(), item_max_size.height());
setPositionTransformations(data,
info->thumbnail, w,
secondThumbnail.adjusted(highlight_margin, highlight_margin, -highlight_margin, -highlight_margin),
Qt::KeepAspectRatio);
effects->drawWindow(w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED,
info->thumbnail, data);
} else {
effects->drawWindow(w,
PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS,
info->thumbnail, data);
}
}
void BoxSwitchEffect::paintDesktopThumbnail(int iDesktop)
{
ItemInfo *info = desktops.value(iDesktop, 0);
if (!info)
return;
ScreenPaintData data;
QRect region;
QRect r = info->area.adjusted(highlight_margin, highlight_margin, -highlight_margin, -highlight_margin);
QSize size = QSize(displayWidth(), displayHeight());
size.scale(r.size(), Qt::KeepAspectRatio);
data.xScale = size.width() / double(displayWidth());
data.yScale = size.height() / double(displayHeight());
int width = int(displayWidth() * data.xScale);
int height = int(displayHeight() * data.yScale);
int x = r.x() + (r.width() - width) / 2;
int y = r.y() + (r.height() - height) / 2;
region = QRect(x, y, width, height);
data.xTranslate = x;
data.yTranslate = y;
effects->paintScreen(PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST,
region, data);
}
void BoxSwitchEffect::paintWindowIcon(EffectWindow* w)
{
ItemInfo *info = windows.value(w, 0);
if (!info)
return;
// Don't render null icons
if (w->icon().isNull()) {
return;
}
int width = w->icon().width();
int height = w->icon().height();
int x = info->area.x() + info->area.width() - width - highlight_margin;
int y = info->area.y() + info->area.height() - height - highlight_margin;
if ((windows.size() % 2 == 0)) {
if (w == right_window) {
// in case of right window the icon has to be painted on the left side of the frame
x = frame_area.x() + info->area.width() * 0.5 - width - highlight_margin;
if (animation) {
if (timeLine.currentValue() <= 0.5) {
if (direction == Left) {
x -= info->area.width() * timeLine.currentValue();
x = qMax(x, frame_area.x());
} else
x += info->area.width() * timeLine.currentValue();
} else {
if (direction == Left)
x += info->area.width() * (1.0 - timeLine.currentValue());
else {
x -= info->area.width() * (1.0 - timeLine.currentValue());
x = qMax(x, frame_area.x());
}
}
}
}
} else {
// during animation the icon of the edge window has to change position
if (animation && w == edge_window) {
if (timeLine.currentValue() < 0.5) {
if (direction == Left)
x += info->area.width() * timeLine.currentValue();
else
x -= info->area.width() * timeLine.currentValue();
} else {
if (direction == Left)
x -= info->area.width() * (1.0 - timeLine.currentValue());
else
x += info->area.width() * (1.0 - timeLine.currentValue());
}
}
}
info->iconFrame->setPosition(QPoint(x, y));
info->iconFrame->render(infiniteRegion(), 1.0, 0.75);
}
void* BoxSwitchEffect::proxy()
{
return &mProxy;
}
void BoxSwitchEffect::activateFromProxy(int mode, bool animate, bool showText, float positioningFactor)
{
if (!mActivated) {
mProxyActivated = true;
mProxyAnimateSwitch = animate;
mProxyShowText = showText;
mPositioningFactor = positioningFactor;
thumbnailFrame->setText(" ");
if ((mode == TabBoxWindowsMode) || (mode == TabBoxWindowsAlternativeMode)) {
if (effects->currentTabBoxWindowList().count() > 0) {
mMode = mode;
effects->refTabBox();
highlight_is_set = false;
animation = false;
scheduled_directions.clear();
right_window = 0;
setActive();
}
} else if (mode == TabBoxDesktopListMode || mode == TabBoxDesktopMode) {
// DesktopMode
if (effects->currentTabBoxDesktopList().count() > 0) {
mMode = mode;
painting_desktop = 0;
effects->refTabBox();
setActive();
}
}
if (!mActivated)
mProxyActivated = false;
}
}
bool BoxSwitchEffect::isActive() const
{
return mActivated;
}
BoxSwitchEffect::ItemInfo::ItemInfo()
: iconFrame(NULL)
{
}
BoxSwitchEffect::ItemInfo::~ItemInfo()
{
delete iconFrame;
}
} // namespace