kwin/effects/slideback/slideback.cpp

342 lines
13 KiB
C++
Raw Normal View History

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2009 Michael Zanetti <michael_zanetti@gmx.net>
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 "slideback.h"
namespace KWin
{
SlideBackEffect::SlideBackEffect()
2011-01-30 14:34:42 +00:00
{
m_tabboxActive = 0;
m_justMapped = m_upmostWindow = NULL;
connect(effects, &EffectsHandler::windowAdded, this, &SlideBackEffect::slotWindowAdded);
connect(effects, &EffectsHandler::windowDeleted, this, &SlideBackEffect::slotWindowDeleted);
connect(effects, &EffectsHandler::windowUnminimized, this, &SlideBackEffect::slotWindowUnminimized);
connect(effects, &EffectsHandler::tabBoxAdded, this, &SlideBackEffect::slotTabBoxAdded);
connect(effects, &EffectsHandler::stackingOrderChanged, this, &SlideBackEffect::slotStackingOrderChanged);
connect(effects, &EffectsHandler::tabBoxClosed, this, &SlideBackEffect::slotTabBoxClosed);
2011-01-30 14:34:42 +00:00
}
void SlideBackEffect::slotStackingOrderChanged()
2011-01-30 14:34:42 +00:00
{
if (effects->activeFullScreenEffect() || m_tabboxActive) {
oldStackingOrder = effects->stackingOrder();
usableOldStackingOrder = usableWindows(oldStackingOrder);
return;
2011-01-30 14:34:42 +00:00
}
EffectWindowList newStackingOrder = effects->stackingOrder(),
usableNewStackingOrder = usableWindows(newStackingOrder);
if (usableNewStackingOrder == usableOldStackingOrder || usableNewStackingOrder.isEmpty()) {
oldStackingOrder = newStackingOrder;
usableOldStackingOrder = usableNewStackingOrder;
return;
2011-01-30 14:34:42 +00:00
}
m_upmostWindow = usableNewStackingOrder.last();
if (m_upmostWindow == m_justMapped ) // a window was added, got on top, stacking changed. Nothing impressive
m_justMapped = 0;
else if (!usableOldStackingOrder.isEmpty() && m_upmostWindow != usableOldStackingOrder.last())
windowRaised(m_upmostWindow);
oldStackingOrder = newStackingOrder;
usableOldStackingOrder = usableNewStackingOrder;
2011-01-30 14:34:42 +00:00
}
void SlideBackEffect::windowRaised(EffectWindow *w)
{
// Determine all windows on top of the activated one
bool currentFound = false;
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * tmp, oldStackingOrder) {
if (!currentFound) {
if (tmp == w) {
currentFound = true;
}
2011-01-30 14:34:42 +00:00
} else {
if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()) {
// Do we have to move it?
2011-01-30 14:34:42 +00:00
if (intersects(w, tmp->geometry())) {
QRect slideRect;
2011-01-30 14:34:42 +00:00
slideRect = getSlideDestination(getModalGroupGeometry(w), tmp->geometry());
effects->setElevatedWindow(tmp, true);
elevatedList.append(tmp);
motionManager.manage(tmp);
motionManager.moveWindow(tmp, slideRect);
destinationList.insert(tmp, slideRect);
coveringWindows.append(tmp);
} else {
//Does it intersect with a moved (elevated) window and do we have to elevate it too?
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * elevatedWindow, elevatedList) {
if (tmp->geometry().intersects(elevatedWindow->geometry())) {
effects->setElevatedWindow(tmp, true);
elevatedList.append(tmp);
break;
}
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
}
if (tmp->isDock() || tmp->keepAbove()) {
effects->setElevatedWindow(tmp, true);
elevatedList.append(tmp);
}
}
2011-01-30 14:34:42 +00:00
}
// If a window is minimized it could happen that the panels stay elevated without any windows sliding.
// clear all elevation settings
2011-01-30 14:34:42 +00:00
if (!motionManager.managingWindows()) {
foreach (EffectWindow * tmp, elevatedList) {
effects->setElevatedWindow(tmp, false);
}
}
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
QRect SlideBackEffect::getSlideDestination(const QRect &windowUnderGeometry, const QRect &windowOverGeometry)
{
// Determine the shortest way:
int leftSlide = windowUnderGeometry.left() - windowOverGeometry.right() - 20;
int rightSlide = windowUnderGeometry.right() - windowOverGeometry.left() + 20;
int upSlide = windowUnderGeometry.top() - windowOverGeometry.bottom() - 20;
int downSlide = windowUnderGeometry.bottom() - windowOverGeometry.top() + 20;
int horizSlide = leftSlide;
2011-01-30 14:34:42 +00:00
if (qAbs(horizSlide) > qAbs(rightSlide)) {
horizSlide = rightSlide;
2011-01-30 14:34:42 +00:00
}
int vertSlide = upSlide;
2011-01-30 14:34:42 +00:00
if (qAbs(vertSlide) > qAbs(downSlide)) {
vertSlide = downSlide;
}
QRect slideRect = windowOverGeometry;
2011-01-30 14:34:42 +00:00
if (qAbs(horizSlide) < qAbs(vertSlide)) {
slideRect.moveLeft(slideRect.x() + horizSlide);
} else {
slideRect.moveTop(slideRect.y() + vertSlide);
}
2011-01-30 14:34:42 +00:00
return slideRect;
}
2011-01-30 14:34:42 +00:00
void SlideBackEffect::prePaintScreen(ScreenPrePaintData &data, int time)
{
if (motionManager.managingWindows()) {
motionManager.calculate(time);
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
}
for (auto const &w : effects->stackingOrder()) {
w->setData(WindowForceBlurRole, QVariant(true));
}
2011-01-30 14:34:42 +00:00
effects->prePaintScreen(data, time);
}
void SlideBackEffect::postPaintScreen()
2011-01-30 14:34:42 +00:00
{
if (motionManager.areWindowsMoving()) {
effects->addRepaintFull();
}
for (auto &w : effects->stackingOrder()) {
w->setData(WindowForceBlurRole, QVariant());
}
2011-01-30 14:34:42 +00:00
effects->postPaintScreen();
}
2011-01-30 14:34:42 +00:00
void SlideBackEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time)
{
if (motionManager.isManaging(w)) {
data.setTransformed();
}
2011-01-30 14:34:42 +00:00
effects->prePaintWindow(w, data, time);
}
void SlideBackEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
{
if (motionManager.isManaging(w)) {
motionManager.apply(w, data);
}
foreach (const QRegion &r, clippedRegions) {
region = region.intersected(r);
}
2011-01-30 14:34:42 +00:00
effects->paintWindow(w, mask, region, data);
for (int i = clippedRegions.count() - 1; i > -1; --i)
PaintClipper::pop(clippedRegions.at(i));
clippedRegions.clear();
}
2011-01-30 14:34:42 +00:00
void SlideBackEffect::postPaintWindow(EffectWindow* w)
{
if (motionManager.isManaging(w)) {
if (destinationList.contains(w)) {
if (!motionManager.isWindowMoving(w)) { // has window reched its destination?
// If we are still intersecting with the upmostWindow it is moving. slide to somewhere else
// restore the stacking order of all windows not intersecting any more except panels
2011-01-30 14:34:42 +00:00
if (coveringWindows.contains(w)) {
EffectWindowList tmpList;
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * tmp, elevatedList) {
QRect elevatedGeometry = tmp->geometry();
2011-01-30 14:34:42 +00:00
if (motionManager.isManaging(tmp)) {
elevatedGeometry = motionManager.transformedGeometry(tmp).toAlignedRect();
}
if (m_upmostWindow && !tmp->isDock() && !tmp->keepAbove() && m_upmostWindow->geometry().intersects(elevatedGeometry)) {
QRect newDestination;
newDestination = getSlideDestination(getModalGroupGeometry(m_upmostWindow), elevatedGeometry);
2011-01-30 14:34:42 +00:00
if (!motionManager.isManaging(tmp)) {
motionManager.manage(tmp);
}
2011-01-30 14:34:42 +00:00
motionManager.moveWindow(tmp, newDestination);
destinationList[tmp] = newDestination;
} else {
if (!tmp->isDock()) {
bool keepElevated = false;
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * elevatedWindow, tmpList) {
if (tmp->geometry().intersects(elevatedWindow->geometry())) {
keepElevated = true;
}
}
2011-01-30 14:34:42 +00:00
if (!keepElevated) {
effects->setElevatedWindow(tmp, false);
elevatedList.removeAll(tmp);
}
}
}
2011-01-30 14:34:42 +00:00
tmpList.append(tmp);
}
2011-01-30 14:34:42 +00:00
} else {
// Move the window back where it belongs
2011-01-30 14:34:42 +00:00
motionManager.moveWindow(w, w->geometry());
destinationList.remove(w);
}
}
2011-01-30 14:34:42 +00:00
} else {
// is window back at its original position?
if (!motionManager.isWindowMoving(w)) {
2011-01-30 14:34:42 +00:00
motionManager.unmanage(w);
effects->addRepaintFull();
}
2011-01-30 14:34:42 +00:00
}
if (coveringWindows.contains(w)) {
// It could happen that there is no aciveWindow() here if the user clicks the close-button on an inactive window.
// Just skip... the window will be removed in windowDeleted() later
if (m_upmostWindow && !intersects(m_upmostWindow, motionManager.transformedGeometry(w).toAlignedRect())) {
2011-01-30 14:34:42 +00:00
coveringWindows.removeAll(w);
if (coveringWindows.isEmpty()) {
// Restore correct stacking order
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * tmp, elevatedList) {
effects->setElevatedWindow(tmp, false);
}
2011-01-30 14:34:42 +00:00
elevatedList.clear();
}
}
}
}
2011-01-30 14:34:42 +00:00
effects->postPaintWindow(w);
}
void SlideBackEffect::slotWindowDeleted(EffectWindow* w)
2011-01-30 14:34:42 +00:00
{
if (w == m_upmostWindow)
m_upmostWindow = 0;
if (w == m_justMapped)
m_justMapped = 0;
2011-01-30 14:34:42 +00:00
usableOldStackingOrder.removeAll(w);
oldStackingOrder.removeAll(w);
coveringWindows.removeAll(w);
elevatedList.removeAll(w);
if (motionManager.isManaging(w)) {
motionManager.unmanage(w);
}
2011-01-30 14:34:42 +00:00
}
void SlideBackEffect::slotWindowAdded(EffectWindow *w)
2011-01-30 14:34:42 +00:00
{
m_justMapped = w;
2011-01-30 14:34:42 +00:00
}
2011-03-06 10:08:19 +00:00
void SlideBackEffect::slotWindowUnminimized(EffectWindow* w)
2011-01-30 14:34:42 +00:00
{
// SlideBack should not be triggered on an unminimized window. For this we need to store the last unminimized window.
m_justMapped = w;
// the stackingOrderChanged() signal came before the window turned an effect window
// usually this is no problem as the change shall not be caught anyway, but
// the window may have changed its stack position, bug #353745
slotStackingOrderChanged();
2011-01-30 14:34:42 +00:00
}
void SlideBackEffect::slotTabBoxAdded()
2011-01-30 14:34:42 +00:00
{
++m_tabboxActive;
2011-01-30 14:34:42 +00:00
}
void SlideBackEffect::slotTabBoxClosed()
2011-01-30 14:34:42 +00:00
{
m_tabboxActive = qMax(m_tabboxActive-1, 0);
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
bool SlideBackEffect::isWindowUsable(EffectWindow* w)
{
return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized()
&& w->isCurrentTab() && w->isPaintingEnabled();
2011-01-30 14:34:42 +00:00
}
2011-01-30 14:34:42 +00:00
bool SlideBackEffect::intersects(EffectWindow* windowUnder, const QRect &windowOverGeometry)
{
QRect windowUnderGeometry = getModalGroupGeometry(windowUnder);
return windowUnderGeometry.intersects(windowOverGeometry);
}
2011-01-30 14:34:42 +00:00
EffectWindowList SlideBackEffect::usableWindows(const EffectWindowList & allWindows)
{
EffectWindowList retList;
auto isWindowVisible = [] (const EffectWindow *window) {
return window && effects->virtualScreenGeometry().intersects(window->geometry());
};
2011-01-30 14:34:42 +00:00
foreach (EffectWindow * tmp, allWindows) {
if (isWindowUsable(tmp) && isWindowVisible(tmp)) {
2011-01-30 14:34:42 +00:00
retList.append(tmp);
}
}
2011-01-30 14:34:42 +00:00
return retList;
}
2011-01-30 14:34:42 +00:00
QRect SlideBackEffect::getModalGroupGeometry(EffectWindow *w)
{
QRect modalGroupGeometry = w->geometry();
2011-01-30 14:34:42 +00:00
if (w->isModal()) {
foreach (EffectWindow * modalWindow, w->mainWindows()) {
modalGroupGeometry = modalGroupGeometry.united(getModalGroupGeometry(modalWindow));
}
}
2011-01-30 14:34:42 +00:00
return modalGroupGeometry;
}
bool SlideBackEffect::isActive() const
{
return motionManager.managingWindows();
}
} //Namespace