kwin/effects/boxswitch/boxswitch.cpp
Thomas Lübking d245035a17 fix tabbing
BUG: 290959
BUG: 265160
BUG: 229292
BUG: 238279
BUG: 290758
BUG: 222831
BUG: 278275
BUG: 245747
BUG: 230000

BUG: 253697
BUG: 230570
BUG: 265977
BUG: 225337
BUG: 225339

REVIEW: 103855
2012-02-11 16:01:41 +01:00

968 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", false);
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_TAB_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 (!mActivated) {
return;
}
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