3604aaf74c
Summary: The screenlock fails on X11 if it can't grab the keyboard. We can't nicely solve the generic case. We can solve the common case of a kwin effect being active. It's not critical, arguably not even desirable to have these effects persist after the screen is locked through an external trigger. We can just close the effect early. Key grabs have to be relased early before the close animation completes so that the locker doesn't have a race based on animation times. It's not ideal, but no worse than the current state for not much work. BUG: 234153 Test Plan: locked screen on a timer opened various effects Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: ngraham, zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D20890
1522 lines
58 KiB
C++
1522 lines
58 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
|
|
Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com>
|
|
Copyright (C) 2009 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
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 "desktopgrid.h"
|
|
// KConfigSkeleton
|
|
#include "desktopgridconfig.h"
|
|
|
|
#include "../presentwindows/presentwindows_proxy.h"
|
|
#include "../effect_builtins.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <KGlobalAccel>
|
|
#include <KLocalizedString>
|
|
#include <netwm_def.h>
|
|
#include <QEvent>
|
|
#include <QMouseEvent>
|
|
#include <QTimer>
|
|
#include <QVector2D>
|
|
#include <QQmlContext>
|
|
#include <QQmlEngine>
|
|
#include <QQuickItem>
|
|
|
|
#include <KWayland/Server/surface_interface.h>
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
// WARNING, TODO: This effect relies on the desktop layout being EWMH-compliant.
|
|
|
|
DesktopGridEffect::DesktopGridEffect()
|
|
: activated(false)
|
|
, timeline()
|
|
, keyboardGrab(false)
|
|
, wasWindowMove(false)
|
|
, wasWindowCopy(false)
|
|
, wasDesktopMove(false)
|
|
, isValidMove(false)
|
|
, windowMove(NULL)
|
|
, windowMoveDiff()
|
|
, gridSize()
|
|
, orientation(Qt::Horizontal)
|
|
, activeCell(1, 1)
|
|
, scale()
|
|
, unscaledBorder()
|
|
, scaledSize()
|
|
, scaledOffset()
|
|
, m_proxy(0)
|
|
, m_activateAction(new QAction(this))
|
|
{
|
|
initConfig<DesktopGridConfig>();
|
|
// Load shortcuts
|
|
QAction* a = m_activateAction;
|
|
a->setObjectName(QStringLiteral("ShowDesktopGrid"));
|
|
a->setText(i18n("Show Desktop Grid"));
|
|
KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << Qt::CTRL + Qt::Key_F8);
|
|
KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << Qt::CTRL + Qt::Key_F8);
|
|
shortcut = KGlobalAccel::self()->shortcut(a);
|
|
effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F8, a);
|
|
effects->registerTouchpadSwipeShortcut(SwipeDirection::Up, a);
|
|
connect(a, &QAction::triggered, this, &DesktopGridEffect::toggle);
|
|
connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &DesktopGridEffect::globalShortcutChanged);
|
|
connect(effects, &EffectsHandler::windowAdded, this, &DesktopGridEffect::slotWindowAdded);
|
|
connect(effects, &EffectsHandler::windowClosed, this, &DesktopGridEffect::slotWindowClosed);
|
|
connect(effects, &EffectsHandler::windowDeleted, this, &DesktopGridEffect::slotWindowDeleted);
|
|
connect(effects, &EffectsHandler::numberDesktopsChanged, this, &DesktopGridEffect::slotNumberDesktopsChanged);
|
|
connect(effects, &EffectsHandler::windowGeometryShapeChanged, this, &DesktopGridEffect::slotWindowGeometryShapeChanged);
|
|
connect(effects, &EffectsHandler::numberScreensChanged, this, &DesktopGridEffect::setup);
|
|
|
|
connect(effects, &EffectsHandler::screenAboutToLock, this, [this]() {
|
|
setActive(false);
|
|
if (keyboardGrab) {
|
|
effects->ungrabKeyboard();
|
|
keyboardGrab = false;
|
|
}
|
|
});
|
|
|
|
// Load all other configuration details
|
|
reconfigure(ReconfigureAll);
|
|
}
|
|
|
|
DesktopGridEffect::~DesktopGridEffect()
|
|
{
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews)
|
|
view->deleteLater();
|
|
m_desktopButtonsViews.clear();
|
|
}
|
|
|
|
void DesktopGridEffect::reconfigure(ReconfigureFlags)
|
|
{
|
|
DesktopGridConfig::self()->read();
|
|
|
|
foreach (ElectricBorder border, borderActivate) {
|
|
effects->unreserveElectricBorder(border, this);
|
|
}
|
|
borderActivate.clear();
|
|
foreach (int i, DesktopGridConfig::borderActivate()) {
|
|
borderActivate.append(ElectricBorder(i));
|
|
effects->reserveElectricBorder(ElectricBorder(i), this);
|
|
}
|
|
|
|
// TODO: rename zoomDuration to duration
|
|
zoomDuration = animationTime(DesktopGridConfig::zoomDuration() != 0 ? DesktopGridConfig::zoomDuration() : 300);
|
|
timeline.setCurveShape(QTimeLine::EaseInOutCurve);
|
|
timeline.setDuration(zoomDuration);
|
|
|
|
border = DesktopGridConfig::borderWidth();
|
|
desktopNameAlignment = Qt::Alignment(DesktopGridConfig::desktopNameAlignment());
|
|
layoutMode = DesktopGridConfig::layoutMode();
|
|
customLayoutRows = DesktopGridConfig::customLayoutRows();
|
|
m_usePresentWindows = DesktopGridConfig::presentWindows();
|
|
|
|
// deactivate and activate all touch border
|
|
const QVector<ElectricBorder> relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom};
|
|
for (auto e : relevantBorders) {
|
|
effects->unregisterTouchBorder(e, m_activateAction);
|
|
}
|
|
const auto touchBorders = DesktopGridConfig::touchBorderActivate();
|
|
for (int i : touchBorders) {
|
|
if (!relevantBorders.contains(ElectricBorder(i))) {
|
|
continue;
|
|
}
|
|
effects->registerTouchBorder(ElectricBorder(i), m_activateAction);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Screen painting
|
|
|
|
void DesktopGridEffect::prePaintScreen(ScreenPrePaintData& data, int time)
|
|
{
|
|
if (timeline.currentValue() != 0 || activated || (isUsingPresentWindows() && isMotionManagerMovingWindows())) {
|
|
if (activated)
|
|
timeline.setCurrentTime(timeline.currentTime() + time);
|
|
else
|
|
timeline.setCurrentTime(timeline.currentTime() - time);
|
|
for (int i = 0; i < effects->numberOfDesktops(); i++) {
|
|
if (i == highlightedDesktop - 1)
|
|
hoverTimeline[i]->setCurrentTime(hoverTimeline[i]->currentTime() + time);
|
|
else
|
|
hoverTimeline[i]->setCurrentTime(hoverTimeline[i]->currentTime() - time);
|
|
}
|
|
if (isUsingPresentWindows()) {
|
|
QList<WindowMotionManager>::iterator i;
|
|
for (i = m_managers.begin(); i != m_managers.end(); ++i)
|
|
(*i).calculate(time);
|
|
}
|
|
// PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once,
|
|
// so with normal screen painting second screen paint would erase parts of the first paint
|
|
if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows()))
|
|
data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
|
|
if (!activated && timeline.currentValue() == 0 && !(isUsingPresentWindows() && isMotionManagerMovingWindows()))
|
|
finish();
|
|
}
|
|
|
|
for (auto const &w : effects->stackingOrder()) {
|
|
w->setData(WindowForceBlurRole, QVariant(true));
|
|
}
|
|
|
|
effects->prePaintScreen(data, time);
|
|
}
|
|
|
|
void DesktopGridEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data)
|
|
{
|
|
if (timeline.currentValue() == 0 && !isUsingPresentWindows()) {
|
|
effects->paintScreen(mask, region, data);
|
|
return;
|
|
}
|
|
for (int desktop = 1; desktop <= effects->numberOfDesktops(); desktop++) {
|
|
ScreenPaintData d = data;
|
|
paintingDesktop = desktop;
|
|
effects->paintScreen(mask, region, d);
|
|
}
|
|
|
|
// paint the add desktop button
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
if (!view->effectWindow) {
|
|
EffectWindow *viewWindow = effects->findWindow(view->winId());
|
|
if (viewWindow) {
|
|
viewWindow->setData(WindowForceBlurRole, QVariant(true));
|
|
view->effectWindow = viewWindow;
|
|
}
|
|
}
|
|
if (view->effectWindow) {
|
|
WindowPaintData d(view->effectWindow);
|
|
d.multiplyOpacity(timeline.currentValue());
|
|
effects->drawWindow(view->effectWindow, PAINT_WINDOW_TRANSLUCENT, infiniteRegion(), d);
|
|
}
|
|
}
|
|
|
|
if (isUsingPresentWindows() && windowMove && wasWindowMove) {
|
|
// the moving window has to be painted on top of all desktops
|
|
QPoint diff = cursorPos() - m_windowMoveStartPoint;
|
|
QRect geo = m_windowMoveGeometry.translated(diff);
|
|
WindowPaintData d(windowMove, data.projectionMatrix());
|
|
d *= QVector2D((qreal)geo.width() / (qreal)windowMove->width(), (qreal)geo.height() / (qreal)windowMove->height());
|
|
d += QPoint(geo.left() - windowMove->x(), geo.top() - windowMove->y());
|
|
effects->drawWindow(windowMove, PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS, infiniteRegion(), d);
|
|
}
|
|
|
|
if (desktopNameAlignment) {
|
|
for (int screen = 0; screen < effects->numScreens(); screen++) {
|
|
QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
|
|
int desktop = 1;
|
|
foreach (EffectFrame * frame, desktopNames) {
|
|
QPointF posTL(scalePos(screenGeom.topLeft(), desktop, screen));
|
|
QPointF posBR(scalePos(screenGeom.bottomRight(), desktop, screen));
|
|
QRect textArea(posTL.x(), posTL.y(), posBR.x() - posTL.x(), posBR.y() - posTL.y());
|
|
textArea.adjust(textArea.width() / 10, textArea.height() / 10,
|
|
-textArea.width() / 10, -textArea.height() / 10);
|
|
int x, y;
|
|
if (desktopNameAlignment & Qt::AlignLeft)
|
|
x = textArea.x();
|
|
else if (desktopNameAlignment & Qt::AlignRight)
|
|
x = textArea.right();
|
|
else
|
|
x = textArea.center().x();
|
|
if (desktopNameAlignment & Qt::AlignTop)
|
|
y = textArea.y();
|
|
else if (desktopNameAlignment & Qt::AlignBottom)
|
|
y = textArea.bottom();
|
|
else
|
|
y = textArea.center().y();
|
|
frame->setPosition(QPoint(x, y));
|
|
frame->render(region, timeline.currentValue(), 0.7);
|
|
++desktop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::postPaintScreen()
|
|
{
|
|
if (activated ? timeline.currentValue() != 1 : timeline.currentValue() != 0)
|
|
effects->addRepaintFull(); // Repaint during zoom
|
|
if (isUsingPresentWindows() && isMotionManagerMovingWindows())
|
|
effects->addRepaintFull();
|
|
if (activated) {
|
|
for (int i = 0; i < effects->numberOfDesktops(); i++) {
|
|
if (hoverTimeline[i]->currentValue() != 0.0 && hoverTimeline[i]->currentValue() != 1.0) {
|
|
// Repaint during soft highlighting
|
|
effects->addRepaintFull();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto &w : effects->stackingOrder()) {
|
|
w->setData(WindowForceBlurRole, QVariant());
|
|
}
|
|
|
|
effects->postPaintScreen();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Window painting
|
|
|
|
void DesktopGridEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time)
|
|
{
|
|
if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) {
|
|
if (w->isOnDesktop(paintingDesktop)) {
|
|
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
|
|
if (w->isMinimized() && isUsingPresentWindows())
|
|
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE);
|
|
data.mask |= PAINT_WINDOW_TRANSFORMED;
|
|
|
|
// Split windows at screen edges
|
|
for (int screen = 0; screen < effects->numScreens(); screen++) {
|
|
QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
|
|
if (w->x() < screenGeom.x())
|
|
data.quads = data.quads.splitAtX(screenGeom.x() - w->x());
|
|
if (w->x() + w->width() > screenGeom.x() + screenGeom.width())
|
|
data.quads = data.quads.splitAtX(screenGeom.x() + screenGeom.width() - w->x());
|
|
if (w->y() < screenGeom.y())
|
|
data.quads = data.quads.splitAtY(screenGeom.y() - w->y());
|
|
if (w->y() + w->height() > screenGeom.y() + screenGeom.height())
|
|
data.quads = data.quads.splitAtY(screenGeom.y() + screenGeom.height() - w->y());
|
|
}
|
|
if (windowMove && wasWindowMove && windowMove->findModal() == w)
|
|
w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
|
|
} else
|
|
w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
|
|
}
|
|
effects->prePaintWindow(w, data, time);
|
|
}
|
|
|
|
void DesktopGridEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
|
|
{
|
|
if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) {
|
|
if (isUsingPresentWindows() && w == windowMove && wasWindowMove &&
|
|
((!wasWindowCopy && sourceDesktop == paintingDesktop) ||
|
|
(sourceDesktop != highlightedDesktop && highlightedDesktop == paintingDesktop))) {
|
|
return; // will be painted on top of all other windows
|
|
}
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
if (view->effectWindow == w) {
|
|
if (!activated && timeline.currentValue() < 0.05) {
|
|
view->hide();
|
|
}
|
|
return; // will be painted on top of all other windows
|
|
}
|
|
}
|
|
|
|
qreal xScale = data.xScale();
|
|
qreal yScale = data.yScale();
|
|
|
|
data.multiplyBrightness(1.0 - (0.3 * (1.0 - hoverTimeline[paintingDesktop - 1]->currentValue())));
|
|
|
|
for (int screen = 0; screen < effects->numScreens(); screen++) {
|
|
QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
|
|
|
|
QRectF transformedGeo = w->geometry();
|
|
// Display all quads on the same screen on the same pass
|
|
WindowQuadList screenQuads;
|
|
bool quadsAdded = false;
|
|
if (isUsingPresentWindows()) {
|
|
WindowMotionManager& manager = m_managers[(paintingDesktop-1)*(effects->numScreens())+screen ];
|
|
if (manager.isManaging(w)) {
|
|
foreach (const WindowQuad & quad, data.quads)
|
|
screenQuads.append(quad);
|
|
transformedGeo = manager.transformedGeometry(w);
|
|
quadsAdded = true;
|
|
if (!manager.areWindowsMoving() && timeline.currentValue() == 1.0)
|
|
mask |= PAINT_WINDOW_LANCZOS;
|
|
} else if (w->screen() != screen)
|
|
quadsAdded = true; // we don't want parts of overlapping windows on the other screen
|
|
if (w->isDesktop())
|
|
quadsAdded = false;
|
|
}
|
|
if (!quadsAdded) {
|
|
foreach (const WindowQuad & quad, data.quads) {
|
|
QRect quadRect(
|
|
w->x() + quad.left(), w->y() + quad.top(),
|
|
quad.right() - quad.left(), quad.bottom() - quad.top()
|
|
);
|
|
if (quadRect.intersects(screenGeom))
|
|
screenQuads.append(quad);
|
|
}
|
|
}
|
|
if (screenQuads.isEmpty())
|
|
continue; // Nothing is being displayed, don't bother
|
|
WindowPaintData d = data;
|
|
d.quads = screenQuads;
|
|
|
|
QPointF newPos = scalePos(transformedGeo.topLeft().toPoint(), paintingDesktop, screen);
|
|
double progress = timeline.currentValue();
|
|
d.setXScale(interpolate(1, xScale * scale[screen] * (float)transformedGeo.width() / (float)w->geometry().width(), progress));
|
|
d.setYScale(interpolate(1, yScale * scale[screen] * (float)transformedGeo.height() / (float)w->geometry().height(), progress));
|
|
d += QPoint(qRound(newPos.x() - w->x()), qRound(newPos.y() - w->y()));
|
|
|
|
if (isUsingPresentWindows() && (w->isDock() || w->isSkipSwitcher())) {
|
|
// fade out panels if present windows is used
|
|
d.multiplyOpacity((1.0 - timeline.currentValue()));
|
|
}
|
|
if (isUsingPresentWindows() && w->isMinimized()) {
|
|
d.multiplyOpacity(timeline.currentValue());
|
|
}
|
|
|
|
if (effects->compositingType() == XRenderCompositing) {
|
|
// More exact clipping as XRender displays the entire window instead of just the quad
|
|
QPointF screenPosF = scalePos(screenGeom.topLeft(), paintingDesktop).toPoint();
|
|
QPoint screenPos(
|
|
qRound(screenPosF.x()),
|
|
qRound(screenPosF.y())
|
|
);
|
|
QSize screenSize(
|
|
qRound(interpolate(screenGeom.width(), scaledSize[screen].width(), progress)),
|
|
qRound(interpolate(screenGeom.height(), scaledSize[screen].height(), progress))
|
|
);
|
|
PaintClipper pc(effects->clientArea(ScreenArea, screen, 0) & QRect(screenPos, screenSize));
|
|
effects->paintWindow(w, mask, region, d);
|
|
} else {
|
|
if (w->isDesktop() && timeline.currentValue() == 1.0) {
|
|
// desktop windows are not in a motion manager and can always be rendered with
|
|
// lanczos sampling except for animations
|
|
mask |= PAINT_WINDOW_LANCZOS;
|
|
}
|
|
effects->paintWindow(w, mask, effects->clientArea(ScreenArea, screen, 0), d);
|
|
}
|
|
}
|
|
} else
|
|
effects->paintWindow(w, mask, region, data);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// User interaction
|
|
|
|
void DesktopGridEffect::slotWindowAdded(EffectWindow* w)
|
|
{
|
|
if (!activated)
|
|
return;
|
|
if (isUsingPresentWindows()) {
|
|
if (!isRelevantWithPresentWindows(w))
|
|
return; // don't add
|
|
foreach (const int i, desktopList(w)) {
|
|
WindowMotionManager& manager = m_managers[ i*effects->numScreens()+w->screen()];
|
|
manager.manage(w);
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager);
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
|
|
void DesktopGridEffect::slotWindowClosed(EffectWindow* w)
|
|
{
|
|
if (!activated && timeline.currentValue() == 0)
|
|
return;
|
|
if (w == windowMove) {
|
|
effects->setElevatedWindow(windowMove, false);
|
|
windowMove = NULL;
|
|
}
|
|
if (isUsingPresentWindows()) {
|
|
foreach (const int i, desktopList(w)) {
|
|
WindowMotionManager& manager = m_managers[i*effects->numScreens()+w->screen()];
|
|
manager.unmanage(w);
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager);
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
|
|
void DesktopGridEffect::slotWindowDeleted(EffectWindow* w)
|
|
{
|
|
if (w == windowMove)
|
|
windowMove = 0;
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
if (view->effectWindow && view->effectWindow == w) {
|
|
view->effectWindow = nullptr;
|
|
break;
|
|
}
|
|
}
|
|
if (isUsingPresentWindows()) {
|
|
for (QList<WindowMotionManager>::iterator it = m_managers.begin(),
|
|
end = m_managers.end(); it != end; ++it) {
|
|
it->unmanage(w);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old)
|
|
{
|
|
Q_UNUSED(old)
|
|
if (!activated)
|
|
return;
|
|
if (w == windowMove && wasWindowMove)
|
|
return;
|
|
if (isUsingPresentWindows()) {
|
|
foreach (const int i, desktopList(w)) {
|
|
WindowMotionManager& manager = m_managers[i*effects->numScreens()+w->screen()];
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
|
|
{
|
|
if ((e->type() != QEvent::MouseMove
|
|
&& e->type() != QEvent::MouseButtonPress
|
|
&& e->type() != QEvent::MouseButtonRelease)
|
|
|| timeline.currentValue() != 1) // Block user input during animations
|
|
return;
|
|
QMouseEvent* me = static_cast< QMouseEvent* >(e);
|
|
if (!(wasWindowMove || wasDesktopMove)) {
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
if (view->geometry().contains(me->pos())) {
|
|
const QPoint widgetPos = view->mapFromGlobal(me->pos());
|
|
QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers());
|
|
view->windowInputMouseEvent(&event);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (e->type() == QEvent::MouseMove) {
|
|
int d = posToDesktop(me->pos());
|
|
if (windowMove != NULL &&
|
|
(me->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) {
|
|
// Handle window moving
|
|
if (!wasWindowMove) { // Activate on move
|
|
if (isUsingPresentWindows()) {
|
|
foreach (const int i, desktopList(windowMove)) {
|
|
WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()];
|
|
if ((i + 1) == sourceDesktop) {
|
|
const QRectF transformedGeo = manager.transformedGeometry(windowMove);
|
|
const QPointF pos = scalePos(transformedGeo.topLeft().toPoint(), sourceDesktop, windowMove->screen());
|
|
const QSize size(scale[windowMove->screen()] *(float)transformedGeo.width(),
|
|
scale[windowMove->screen()] *(float)transformedGeo.height());
|
|
m_windowMoveGeometry = QRect(pos.toPoint(), size);
|
|
m_windowMoveStartPoint = me->pos();
|
|
}
|
|
manager.unmanage(windowMove);
|
|
if (EffectWindow* modal = windowMove->findModal()) {
|
|
if (manager.isManaging(modal))
|
|
manager.unmanage(modal);
|
|
}
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), windowMove->screen(), manager);
|
|
}
|
|
wasWindowMove = true;
|
|
}
|
|
}
|
|
if (windowMove->isMovable() && !isUsingPresentWindows()) {
|
|
wasWindowMove = true;
|
|
int screen = effects->screenNumber(me->pos());
|
|
effects->moveWindow(windowMove, unscalePos(me->pos(), NULL) + windowMoveDiff, true, 1.0 / scale[screen]);
|
|
}
|
|
if (wasWindowMove) {
|
|
if (effects->waylandDisplay() && (me->modifiers() & Qt::ControlModifier)) {
|
|
wasWindowCopy = true;
|
|
effects->defineCursor(Qt::DragCopyCursor);
|
|
} else {
|
|
wasWindowCopy = false;
|
|
effects->defineCursor(Qt::ClosedHandCursor);
|
|
}
|
|
if (d != highlightedDesktop) {
|
|
auto desktops = windowMove->desktops();
|
|
if (!desktops.contains(d)) {
|
|
desktops.append(d);
|
|
}
|
|
if (highlightedDesktop != sourceDesktop || !wasWindowCopy) {
|
|
desktops.removeOne(highlightedDesktop);
|
|
}
|
|
effects->windowToDesktops(windowMove, desktops);
|
|
const int screen = effects->screenNumber(me->pos());
|
|
if (screen != windowMove->screen())
|
|
effects->windowToScreen(windowMove, screen);
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
} else if ((me->buttons() & Qt::LeftButton) && !wasDesktopMove &&
|
|
(me->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) {
|
|
wasDesktopMove = true;
|
|
effects->defineCursor(Qt::ClosedHandCursor);
|
|
}
|
|
if (d != highlightedDesktop) { // Highlight desktop
|
|
if ((me->buttons() & Qt::LeftButton) && isValidMove && !wasWindowMove && d <= effects->numberOfDesktops()) {
|
|
EffectWindowList windows = effects->stackingOrder();
|
|
EffectWindowList stack[3];
|
|
for (EffectWindowList::const_iterator it = windows.constBegin(),
|
|
end = windows.constEnd(); it != end; ++it) {
|
|
EffectWindow *w = const_cast<EffectWindow*>(*it); // we're not really touching it here but below
|
|
if (w->isOnAllDesktops())
|
|
continue;
|
|
if (w->isOnDesktop(highlightedDesktop))
|
|
stack[0] << w;
|
|
if (w->isOnDesktop(d))
|
|
stack[1] << w;
|
|
if (w->isOnDesktop(m_originalMovingDesktop))
|
|
stack[2] << w;
|
|
}
|
|
const int desks[4] = {highlightedDesktop, d, m_originalMovingDesktop, highlightedDesktop};
|
|
for (int i = 0; i < 3; ++i ) {
|
|
if (desks[i] == desks[i+1])
|
|
continue;
|
|
foreach (EffectWindow *w, stack[i]) {
|
|
auto desktops = w->desktops();
|
|
desktops.removeOne(desks[i]);
|
|
desktops.append(desks[i+1]);
|
|
effects->windowToDesktops(w, desktops);
|
|
|
|
if (isUsingPresentWindows()) {
|
|
m_managers[(desks[i]-1)*(effects->numScreens()) + w->screen()].unmanage(w);
|
|
m_managers[(desks[i+1]-1)*(effects->numScreens()) + w->screen()].manage(w);
|
|
}
|
|
}
|
|
}
|
|
if (isUsingPresentWindows()) {
|
|
for (int i = 0; i < effects->numScreens(); i++) {
|
|
for (int j = 0; j < 3; ++j) {
|
|
WindowMotionManager& manager = m_managers[(desks[j]-1)*(effects->numScreens()) + i ];
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), i, manager);
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
}
|
|
setHighlightedDesktop(d);
|
|
}
|
|
}
|
|
if (e->type() == QEvent::MouseButtonPress) {
|
|
if (me->buttons() == Qt::LeftButton) {
|
|
isValidMove = true;
|
|
dragStartPos = me->pos();
|
|
sourceDesktop = posToDesktop(me->pos());
|
|
bool isDesktop = (me->modifiers() & Qt::ShiftModifier);
|
|
EffectWindow* w = isDesktop ? NULL : windowAt(me->pos());
|
|
if (w != NULL)
|
|
isDesktop = w->isDesktop();
|
|
if (isDesktop)
|
|
m_originalMovingDesktop = posToDesktop(me->pos());
|
|
else
|
|
m_originalMovingDesktop = 0;
|
|
if (w != NULL && !w->isDesktop() && (w->isMovable() || w->isMovableAcrossScreens() || isUsingPresentWindows())) {
|
|
// Prepare it for moving
|
|
windowMoveDiff = w->pos() - unscalePos(me->pos(), NULL);
|
|
windowMove = w;
|
|
effects->setElevatedWindow(windowMove, true);
|
|
}
|
|
} else if ((me->buttons() == Qt::MidButton || me->buttons() == Qt::RightButton) && windowMove == NULL) {
|
|
EffectWindow* w = windowAt(me->pos());
|
|
if (w && w->isDesktop()) {
|
|
w = nullptr;
|
|
}
|
|
if (w != NULL) {
|
|
const int desktop = posToDesktop(me->pos());
|
|
if (w->isOnAllDesktops()) {
|
|
effects->windowToDesktop(w, desktop);
|
|
} else {
|
|
effects->windowToDesktop(w, NET::OnAllDesktops);
|
|
}
|
|
const bool isOnAllDesktops = w->isOnAllDesktops();
|
|
if (isUsingPresentWindows()) {
|
|
for (int i = 0; i < effects->numberOfDesktops(); i++) {
|
|
if (i != desktop - 1) {
|
|
WindowMotionManager& manager = m_managers[ i*effects->numScreens() + w->screen()];
|
|
if (isOnAllDesktops)
|
|
manager.manage(w);
|
|
else
|
|
manager.unmanage(w);
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager);
|
|
}
|
|
}
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
}
|
|
}
|
|
if (e->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton) {
|
|
isValidMove = false;
|
|
if (windowMove)
|
|
effects->activateWindow(windowMove);
|
|
if (wasWindowMove || wasDesktopMove) { // reset pointer
|
|
effects->defineCursor(Qt::PointingHandCursor);
|
|
} else { // click -> exit
|
|
const int desk = posToDesktop(me->pos());
|
|
if (desk > effects->numberOfDesktops())
|
|
return; // don't quit when missing desktop
|
|
setCurrentDesktop(desk);
|
|
setActive(false);
|
|
}
|
|
if (windowMove) {
|
|
if (wasWindowMove && isUsingPresentWindows()) {
|
|
const int targetDesktop = posToDesktop(cursorPos());
|
|
foreach (const int i, desktopList(windowMove)) {
|
|
WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()];
|
|
manager.manage(windowMove);
|
|
if (EffectWindow* modal = windowMove->findModal())
|
|
manager.manage(modal);
|
|
if (i + 1 == targetDesktop) {
|
|
// for the desktop the window is dropped on, we use the current geometry
|
|
manager.setTransformedGeometry(windowMove, moveGeometryToDesktop(targetDesktop));
|
|
}
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), windowMove->screen(), manager);
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
effects->setElevatedWindow(windowMove, false);
|
|
windowMove = NULL;
|
|
}
|
|
wasWindowMove = false;
|
|
wasWindowCopy = false;
|
|
wasDesktopMove = false;
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::grabbedKeyboardEvent(QKeyEvent* e)
|
|
{
|
|
if (timeline.currentValue() != 1) // Block user input during animations
|
|
return;
|
|
if (windowMove != NULL)
|
|
return;
|
|
if (e->type() == QEvent::KeyPress) {
|
|
// check for global shortcuts
|
|
// HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
|
|
if (shortcut.contains(e->key() + e->modifiers())) {
|
|
toggle();
|
|
return;
|
|
}
|
|
|
|
int desktop = -1;
|
|
// switch by F<number> or just <number>
|
|
if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35)
|
|
desktop = e->key() - Qt::Key_F1 + 1;
|
|
else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9)
|
|
desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0;
|
|
if (desktop != -1) {
|
|
if (desktop <= effects->numberOfDesktops()) {
|
|
setHighlightedDesktop(desktop);
|
|
setCurrentDesktop(desktop);
|
|
setActive(false);
|
|
}
|
|
return;
|
|
}
|
|
switch(e->key()) {
|
|
// Wrap only on autorepeat
|
|
case Qt::Key_Left:
|
|
setHighlightedDesktop(desktopToLeft(highlightedDesktop, !e->isAutoRepeat()));
|
|
break;
|
|
case Qt::Key_Right:
|
|
setHighlightedDesktop(desktopToRight(highlightedDesktop, !e->isAutoRepeat()));
|
|
break;
|
|
case Qt::Key_Up:
|
|
setHighlightedDesktop(desktopUp(highlightedDesktop, !e->isAutoRepeat()));
|
|
break;
|
|
case Qt::Key_Down:
|
|
setHighlightedDesktop(desktopDown(highlightedDesktop, !e->isAutoRepeat()));
|
|
break;
|
|
case Qt::Key_Escape:
|
|
setActive(false);
|
|
return;
|
|
case Qt::Key_Enter:
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Space:
|
|
setCurrentDesktop(highlightedDesktop);
|
|
setActive(false);
|
|
return;
|
|
case Qt::Key_Plus:
|
|
slotAddDesktop();
|
|
break;
|
|
case Qt::Key_Minus:
|
|
slotRemoveDesktop();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DesktopGridEffect::borderActivated(ElectricBorder border)
|
|
{
|
|
if (!borderActivate.contains(border))
|
|
return false;
|
|
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
|
|
return true;
|
|
toggle();
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helper functions
|
|
|
|
// Transform a point to its position on the scaled grid
|
|
QPointF DesktopGridEffect::scalePos(const QPoint& pos, int desktop, int screen) const
|
|
{
|
|
if (screen == -1)
|
|
screen = effects->screenNumber(pos);
|
|
QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
|
|
QPoint desktopCell;
|
|
if (orientation == Qt::Horizontal) {
|
|
desktopCell.setX((desktop - 1) % gridSize.width() + 1);
|
|
desktopCell.setY((desktop - 1) / gridSize.width() + 1);
|
|
} else {
|
|
desktopCell.setX((desktop - 1) / gridSize.height() + 1);
|
|
desktopCell.setY((desktop - 1) % gridSize.height() + 1);
|
|
}
|
|
|
|
double progress = timeline.currentValue();
|
|
QPointF point(
|
|
interpolate(
|
|
(
|
|
(screenGeom.width() + unscaledBorder[screen]) *(desktopCell.x() - 1)
|
|
- (screenGeom.width() + unscaledBorder[screen]) *(activeCell.x() - 1)
|
|
) + pos.x(),
|
|
(
|
|
(scaledSize[screen].width() + border) *(desktopCell.x() - 1)
|
|
+ scaledOffset[screen].x()
|
|
+ (pos.x() - screenGeom.x()) * scale[screen]
|
|
),
|
|
progress),
|
|
interpolate(
|
|
(
|
|
(screenGeom.height() + unscaledBorder[screen]) *(desktopCell.y() - 1)
|
|
- (screenGeom.height() + unscaledBorder[screen]) *(activeCell.y() - 1)
|
|
) + pos.y(),
|
|
(
|
|
(scaledSize[screen].height() + border) *(desktopCell.y() - 1)
|
|
+ scaledOffset[screen].y()
|
|
+ (pos.y() - screenGeom.y()) * scale[screen]
|
|
),
|
|
progress)
|
|
);
|
|
|
|
return point;
|
|
}
|
|
|
|
// Detransform a point to its position on the full grid
|
|
// TODO: Doesn't correctly interpolate (Final position is correct though), don't forget to copy to posToDesktop()
|
|
QPoint DesktopGridEffect::unscalePos(const QPoint& pos, int* desktop) const
|
|
{
|
|
int screen = effects->screenNumber(pos);
|
|
QRect screenGeom = effects->clientArea(ScreenArea, screen, 0);
|
|
|
|
//double progress = timeline.currentValue();
|
|
double scaledX = /*interpolate(
|
|
( pos.x() - screenGeom.x() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.width() + unscaledBorder[screen] ) + activeCell.x() - 1,*/
|
|
(pos.x() - scaledOffset[screen].x() + double(border) / 2.0) / (scaledSize[screen].width() + border)/*,
|
|
progress )*/;
|
|
double scaledY = /*interpolate(
|
|
( pos.y() - screenGeom.y() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.height() + unscaledBorder[screen] ) + activeCell.y() - 1,*/
|
|
(pos.y() - scaledOffset[screen].y() + double(border) / 2.0) / (scaledSize[screen].height() + border)/*,
|
|
progress )*/;
|
|
int gx = qBound(0, int(scaledX), gridSize.width() - 1); // Zero-based
|
|
int gy = qBound(0, int(scaledY), gridSize.height() - 1);
|
|
scaledX -= gx;
|
|
scaledY -= gy;
|
|
if (desktop != NULL) {
|
|
if (orientation == Qt::Horizontal)
|
|
*desktop = gy * gridSize.width() + gx + 1;
|
|
else
|
|
*desktop = gx * gridSize.height() + gy + 1;
|
|
}
|
|
|
|
return QPoint(
|
|
qBound(
|
|
screenGeom.x(),
|
|
qRound(
|
|
scaledX * (screenGeom.width() + unscaledBorder[screen])
|
|
- unscaledBorder[screen] / 2.0
|
|
+ screenGeom.x()
|
|
),
|
|
screenGeom.right()
|
|
),
|
|
qBound(
|
|
screenGeom.y(),
|
|
qRound(
|
|
scaledY * (screenGeom.height() + unscaledBorder[screen])
|
|
- unscaledBorder[screen] / 2.0
|
|
+ screenGeom.y()
|
|
),
|
|
screenGeom.bottom()
|
|
)
|
|
);
|
|
}
|
|
|
|
int DesktopGridEffect::posToDesktop(const QPoint& pos) const
|
|
{
|
|
// Copied from unscalePos()
|
|
int screen = effects->screenNumber(pos);
|
|
|
|
double scaledX = (pos.x() - scaledOffset[screen].x() + double(border) / 2.0) / (scaledSize[screen].width() + border);
|
|
double scaledY = (pos.y() - scaledOffset[screen].y() + double(border) / 2.0) / (scaledSize[screen].height() + border);
|
|
int gx = qBound(0, int(scaledX), gridSize.width() - 1); // Zero-based
|
|
int gy = qBound(0, int(scaledY), gridSize.height() - 1);
|
|
if (orientation == Qt::Horizontal)
|
|
return gy * gridSize.width() + gx + 1;
|
|
return gx * gridSize.height() + gy + 1;
|
|
}
|
|
|
|
EffectWindow* DesktopGridEffect::windowAt(QPoint pos) const
|
|
{
|
|
// Get stacking order top first
|
|
EffectWindowList windows = effects->stackingOrder();
|
|
EffectWindowList::Iterator begin = windows.begin();
|
|
EffectWindowList::Iterator end = windows.end();
|
|
--end;
|
|
while (begin < end)
|
|
qSwap(*begin++, *end--);
|
|
|
|
int desktop;
|
|
pos = unscalePos(pos, &desktop);
|
|
if (desktop > effects->numberOfDesktops())
|
|
return NULL;
|
|
if (isUsingPresentWindows()) {
|
|
const int screen = effects->screenNumber(pos);
|
|
EffectWindow *w =
|
|
m_managers.at((desktop - 1) * (effects->numScreens()) + screen).windowAtPoint(pos, false);
|
|
if (w)
|
|
return w;
|
|
foreach (EffectWindow * w, windows) {
|
|
if (w->isOnDesktop(desktop) && w->isDesktop() && w->geometry().contains(pos))
|
|
return w;
|
|
}
|
|
} else {
|
|
foreach (EffectWindow * w, windows) {
|
|
if (w->isOnDesktop(desktop) && w->isOnCurrentActivity() && !w->isMinimized() && w->geometry().contains(pos))
|
|
return w;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void DesktopGridEffect::setCurrentDesktop(int desktop)
|
|
{
|
|
if (orientation == Qt::Horizontal) {
|
|
activeCell.setX((desktop - 1) % gridSize.width() + 1);
|
|
activeCell.setY((desktop - 1) / gridSize.width() + 1);
|
|
} else {
|
|
activeCell.setX((desktop - 1) / gridSize.height() + 1);
|
|
activeCell.setY((desktop - 1) % gridSize.height() + 1);
|
|
}
|
|
if (effects->currentDesktop() != desktop)
|
|
effects->setCurrentDesktop(desktop);
|
|
}
|
|
|
|
void DesktopGridEffect::setHighlightedDesktop(int d)
|
|
{
|
|
if (d == highlightedDesktop || d <= 0 || d > effects->numberOfDesktops())
|
|
return;
|
|
if (highlightedDesktop > 0 && highlightedDesktop <= hoverTimeline.count())
|
|
hoverTimeline[highlightedDesktop-1]->setCurrentTime(qMin(hoverTimeline[highlightedDesktop-1]->currentTime(),
|
|
hoverTimeline[highlightedDesktop-1]->duration()));
|
|
highlightedDesktop = d;
|
|
if (highlightedDesktop <= hoverTimeline.count())
|
|
hoverTimeline[highlightedDesktop-1]->setCurrentTime(qMax(hoverTimeline[highlightedDesktop-1]->currentTime(), 0));
|
|
effects->addRepaintFull();
|
|
}
|
|
|
|
int DesktopGridEffect::desktopToRight(int desktop, bool wrap) const
|
|
{
|
|
// Copied from Workspace::desktopToRight()
|
|
int dt = desktop - 1;
|
|
if (orientation == Qt::Vertical) {
|
|
dt += gridSize.height();
|
|
if (dt >= effects->numberOfDesktops()) {
|
|
if (wrap)
|
|
dt -= effects->numberOfDesktops();
|
|
else
|
|
return desktop;
|
|
}
|
|
} else {
|
|
int d = (dt % gridSize.width()) + 1;
|
|
if (d >= gridSize.width()) {
|
|
if (wrap)
|
|
d -= gridSize.width();
|
|
else
|
|
return desktop;
|
|
}
|
|
dt = dt - (dt % gridSize.width()) + d;
|
|
}
|
|
return dt + 1;
|
|
}
|
|
|
|
int DesktopGridEffect::desktopToLeft(int desktop, bool wrap) const
|
|
{
|
|
// Copied from Workspace::desktopToLeft()
|
|
int dt = desktop - 1;
|
|
if (orientation == Qt::Vertical) {
|
|
dt -= gridSize.height();
|
|
if (dt < 0) {
|
|
if (wrap)
|
|
dt += effects->numberOfDesktops();
|
|
else
|
|
return desktop;
|
|
}
|
|
} else {
|
|
int d = (dt % gridSize.width()) - 1;
|
|
if (d < 0) {
|
|
if (wrap)
|
|
d += gridSize.width();
|
|
else
|
|
return desktop;
|
|
}
|
|
dt = dt - (dt % gridSize.width()) + d;
|
|
}
|
|
return dt + 1;
|
|
}
|
|
|
|
int DesktopGridEffect::desktopUp(int desktop, bool wrap) const
|
|
{
|
|
// Copied from Workspace::desktopUp()
|
|
int dt = desktop - 1;
|
|
if (orientation == Qt::Horizontal) {
|
|
dt -= gridSize.width();
|
|
if (dt < 0) {
|
|
if (wrap)
|
|
dt += effects->numberOfDesktops();
|
|
else
|
|
return desktop;
|
|
}
|
|
} else {
|
|
int d = (dt % gridSize.height()) - 1;
|
|
if (d < 0) {
|
|
if (wrap)
|
|
d += gridSize.height();
|
|
else
|
|
return desktop;
|
|
}
|
|
dt = dt - (dt % gridSize.height()) + d;
|
|
}
|
|
return dt + 1;
|
|
}
|
|
|
|
int DesktopGridEffect::desktopDown(int desktop, bool wrap) const
|
|
{
|
|
// Copied from Workspace::desktopDown()
|
|
int dt = desktop - 1;
|
|
if (orientation == Qt::Horizontal) {
|
|
dt += gridSize.width();
|
|
if (dt >= effects->numberOfDesktops()) {
|
|
if (wrap)
|
|
dt -= effects->numberOfDesktops();
|
|
else
|
|
return desktop;
|
|
}
|
|
} else {
|
|
int d = (dt % gridSize.height()) + 1;
|
|
if (d >= gridSize.height()) {
|
|
if (wrap)
|
|
d -= gridSize.height();
|
|
else
|
|
return desktop;
|
|
}
|
|
dt = dt - (dt % gridSize.height()) + d;
|
|
}
|
|
return dt + 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Activation
|
|
|
|
void DesktopGridEffect::toggle()
|
|
{
|
|
setActive(!activated);
|
|
}
|
|
|
|
void DesktopGridEffect::setActive(bool active)
|
|
{
|
|
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
|
|
return; // Only one fullscreen effect at a time thanks
|
|
if (active && isMotionManagerMovingWindows())
|
|
return; // Still moving windows from last usage - don't activate
|
|
if (activated == active)
|
|
return; // Already in that state
|
|
|
|
activated = active;
|
|
if (activated) {
|
|
effects->setShowingDesktop(false);
|
|
if (timeline.currentValue() == 0)
|
|
setup();
|
|
} else {
|
|
if (isUsingPresentWindows()) {
|
|
QList<WindowMotionManager>::iterator it;
|
|
for (it = m_managers.begin(); it != m_managers.end(); ++it) {
|
|
foreach (EffectWindow * w, (*it).managedWindows()) {
|
|
(*it).moveWindow(w, w->geometry());
|
|
}
|
|
}
|
|
}
|
|
QTimer::singleShot(zoomDuration + 1, this,
|
|
[this] {
|
|
if (activated)
|
|
return;
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
view->hide();
|
|
}
|
|
}
|
|
);
|
|
setHighlightedDesktop(effects->currentDesktop()); // Ensure selected desktop is highlighted
|
|
}
|
|
effects->addRepaintFull();
|
|
}
|
|
|
|
void DesktopGridEffect::setup()
|
|
{
|
|
if (!isActive())
|
|
return;
|
|
if (!keyboardGrab) {
|
|
keyboardGrab = effects->grabKeyboard(this);
|
|
effects->startMouseInterception(this, Qt::PointingHandCursor);
|
|
effects->setActiveFullScreenEffect(this);
|
|
}
|
|
setHighlightedDesktop(effects->currentDesktop());
|
|
|
|
// Soft highlighting
|
|
qDeleteAll(hoverTimeline);
|
|
hoverTimeline.clear();
|
|
for (int i = 0; i < effects->numberOfDesktops(); i++) {
|
|
QTimeLine *newTimeline = new QTimeLine(zoomDuration, this);
|
|
newTimeline->setCurveShape(QTimeLine::EaseInOutCurve);
|
|
hoverTimeline.append(newTimeline);
|
|
}
|
|
hoverTimeline[effects->currentDesktop() - 1]->setCurrentTime(hoverTimeline[effects->currentDesktop() - 1]->duration());
|
|
|
|
// Create desktop name textures if enabled
|
|
if (desktopNameAlignment) {
|
|
QFont font;
|
|
font.setBold(true);
|
|
font.setPointSize(12);
|
|
for (int i = 0; i < effects->numberOfDesktops(); i++) {
|
|
EffectFrame* frame = effects->effectFrame(EffectFrameUnstyled, false);
|
|
frame->setFont(font);
|
|
frame->setText(effects->desktopName(i + 1));
|
|
frame->setAlignment(desktopNameAlignment);
|
|
desktopNames.append(frame);
|
|
}
|
|
}
|
|
setupGrid();
|
|
setCurrentDesktop(effects->currentDesktop());
|
|
|
|
// setup the motion managers
|
|
if (m_usePresentWindows)
|
|
m_proxy = static_cast<PresentWindowsEffectProxy*>(effects->getProxy(BuiltInEffects::nameForEffect(BuiltInEffect::PresentWindows)));
|
|
if (isUsingPresentWindows()) {
|
|
m_proxy->reCreateGrids(); // revalidation on multiscreen, bug #351724
|
|
for (int i = 1; i <= effects->numberOfDesktops(); i++) {
|
|
for (int j = 0; j < effects->numScreens(); j++) {
|
|
WindowMotionManager manager;
|
|
foreach (EffectWindow * w, effects->stackingOrder()) {
|
|
if (w->isOnDesktop(i) && w->screen() == j &&isRelevantWithPresentWindows(w)) {
|
|
manager.manage(w);
|
|
}
|
|
}
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager);
|
|
m_managers.append(manager);
|
|
}
|
|
}
|
|
}
|
|
bool enableAdd = effects->numberOfDesktops() < 20;
|
|
bool enableRemove = effects->numberOfDesktops() > 1;
|
|
|
|
QVector<DesktopButtonsView*>::iterator it = m_desktopButtonsViews.begin();
|
|
const int n = DesktopGridConfig::showAddRemove() ? effects->numScreens() : 0;
|
|
for (int i = 0; i < n; ++i) {
|
|
DesktopButtonsView *view;
|
|
if (it == m_desktopButtonsViews.end()) {
|
|
view = new DesktopButtonsView();
|
|
m_desktopButtonsViews.append(view);
|
|
it = m_desktopButtonsViews.end(); // changed through insert!
|
|
connect(view, &DesktopButtonsView::addDesktop, this, &DesktopGridEffect::slotAddDesktop);
|
|
connect(view, &DesktopButtonsView::removeDesktop, this, &DesktopGridEffect::slotRemoveDesktop);
|
|
} else {
|
|
view = *it;
|
|
++it;
|
|
}
|
|
view->setAddDesktopEnabled(enableAdd);
|
|
view->setRemoveDesktopEnabled(enableRemove);
|
|
const QRect screenRect = effects->clientArea(FullScreenArea, i, 1);
|
|
view->show(); // pseudo show must happen before geometry changes
|
|
view->setPosition(screenRect.right() - border/3 - view->width(),
|
|
screenRect.bottom() - border/3 - view->height());
|
|
}
|
|
while (it != m_desktopButtonsViews.end()) {
|
|
(*it)->deleteLater();
|
|
it = m_desktopButtonsViews.erase(it);
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::setupGrid()
|
|
{
|
|
// We need these variables for every paint so lets cache them
|
|
int x, y;
|
|
int numDesktops = effects->numberOfDesktops();
|
|
switch(layoutMode) {
|
|
default:
|
|
case LayoutPager:
|
|
orientation = Qt::Horizontal;
|
|
gridSize = effects->desktopGridSize();
|
|
// sanity check: pager may report incorrect size in case of one desktop
|
|
if (numDesktops == 1) {
|
|
gridSize = QSize(1, 1);
|
|
}
|
|
break;
|
|
case LayoutAutomatic:
|
|
y = sqrt(float(numDesktops)) + 0.5;
|
|
x = float(numDesktops) / float(y) + 0.5;
|
|
if (x * y < numDesktops)
|
|
x++;
|
|
orientation = Qt::Horizontal;
|
|
gridSize.setWidth(x);
|
|
gridSize.setHeight(y);
|
|
break;
|
|
case LayoutCustom:
|
|
orientation = Qt::Horizontal;
|
|
gridSize.setWidth(ceil(effects->numberOfDesktops() / double(customLayoutRows)));
|
|
gridSize.setHeight(customLayoutRows);
|
|
break;
|
|
}
|
|
scale.clear();
|
|
unscaledBorder.clear();
|
|
scaledSize.clear();
|
|
scaledOffset.clear();
|
|
for (int i = 0; i < effects->numScreens(); i++) {
|
|
QRect geom = effects->clientArea(ScreenArea, i, 0);
|
|
double sScale;
|
|
if (gridSize.width() > gridSize.height())
|
|
sScale = (geom.width() - border * (gridSize.width() + 1)) / double(geom.width() * gridSize.width());
|
|
else
|
|
sScale = (geom.height() - border * (gridSize.height() + 1)) / double(geom.height() * gridSize.height());
|
|
double sBorder = border / sScale;
|
|
QSizeF size(
|
|
double(geom.width()) * sScale,
|
|
double(geom.height()) * sScale
|
|
);
|
|
QPointF offset(
|
|
geom.x() + (geom.width() - size.width() * gridSize.width() - border *(gridSize.width() - 1)) / 2.0,
|
|
geom.y() + (geom.height() - size.height() * gridSize.height() - border *(gridSize.height() - 1)) / 2.0
|
|
);
|
|
scale.append(sScale);
|
|
unscaledBorder.append(sBorder);
|
|
scaledSize.append(size);
|
|
scaledOffset.append(offset);
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::finish()
|
|
{
|
|
if (desktopNameAlignment) {
|
|
qDeleteAll(desktopNames);
|
|
desktopNames.clear();
|
|
}
|
|
|
|
if (keyboardGrab)
|
|
effects->ungrabKeyboard();
|
|
keyboardGrab = false;
|
|
effects->stopMouseInterception(this);
|
|
effects->setActiveFullScreenEffect(0);
|
|
if (isUsingPresentWindows()) {
|
|
while (!m_managers.isEmpty()) {
|
|
m_managers.first().unmanageAll();
|
|
m_managers.removeFirst();
|
|
}
|
|
m_proxy = 0;
|
|
}
|
|
}
|
|
|
|
void DesktopGridEffect::globalShortcutChanged(QAction *action, const QKeySequence& seq)
|
|
{
|
|
if (action->objectName() != QStringLiteral("ShowDesktopGrid")) {
|
|
return;
|
|
}
|
|
shortcut.clear();
|
|
shortcut.append(seq);
|
|
}
|
|
|
|
bool DesktopGridEffect::isMotionManagerMovingWindows() const
|
|
{
|
|
if (isUsingPresentWindows()) {
|
|
QList<WindowMotionManager>::const_iterator it;
|
|
for (it = m_managers.begin(); it != m_managers.end(); ++it) {
|
|
if ((*it).areWindowsMoving())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DesktopGridEffect::isUsingPresentWindows() const
|
|
{
|
|
return (m_proxy != NULL);
|
|
}
|
|
|
|
// transforms the geometry of the moved window to a geometry on the desktop
|
|
// internal method only used when a window is dropped onto a desktop
|
|
QRectF DesktopGridEffect::moveGeometryToDesktop(int desktop) const
|
|
{
|
|
QPointF point = unscalePos(m_windowMoveGeometry.topLeft() + cursorPos() - m_windowMoveStartPoint);
|
|
const double scaleFactor = scale[ windowMove->screen()];
|
|
if (posToDesktop(m_windowMoveGeometry.topLeft() + cursorPos() - m_windowMoveStartPoint) != desktop) {
|
|
// topLeft is not on the desktop - check other corners
|
|
// if all corners are not on the desktop the window is bigger than the desktop - no matter what it will look strange
|
|
if (posToDesktop(m_windowMoveGeometry.topRight() + cursorPos() - m_windowMoveStartPoint) == desktop) {
|
|
point = unscalePos(m_windowMoveGeometry.topRight() + cursorPos() - m_windowMoveStartPoint) -
|
|
QPointF(m_windowMoveGeometry.width(), 0) / scaleFactor;
|
|
} else if (posToDesktop(m_windowMoveGeometry.bottomLeft() + cursorPos() - m_windowMoveStartPoint) == desktop) {
|
|
point = unscalePos(m_windowMoveGeometry.bottomLeft() + cursorPos() - m_windowMoveStartPoint) -
|
|
QPointF(0, m_windowMoveGeometry.height()) / scaleFactor;
|
|
} else if (posToDesktop(m_windowMoveGeometry.bottomRight() + cursorPos() - m_windowMoveStartPoint) == desktop) {
|
|
point = unscalePos(m_windowMoveGeometry.bottomRight() + cursorPos() - m_windowMoveStartPoint) -
|
|
QPointF(m_windowMoveGeometry.width(), m_windowMoveGeometry.height()) / scaleFactor;
|
|
}
|
|
}
|
|
return QRectF(point, m_windowMoveGeometry.size() / scaleFactor);
|
|
}
|
|
|
|
void DesktopGridEffect::slotAddDesktop()
|
|
{
|
|
effects->setNumberOfDesktops(effects->numberOfDesktops() + 1);
|
|
}
|
|
|
|
void DesktopGridEffect::slotRemoveDesktop()
|
|
{
|
|
effects->setNumberOfDesktops(effects->numberOfDesktops() - 1);
|
|
}
|
|
|
|
void DesktopGridEffect::slotNumberDesktopsChanged(uint old)
|
|
{
|
|
if (!activated)
|
|
return;
|
|
const uint desktop = effects->numberOfDesktops();
|
|
bool enableAdd = desktop < 20;
|
|
bool enableRemove = desktop > 1;
|
|
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
|
|
view->setAddDesktopEnabled(enableAdd);
|
|
view->setRemoveDesktopEnabled(enableRemove);
|
|
}
|
|
if (old < desktop)
|
|
desktopsAdded(old);
|
|
else
|
|
desktopsRemoved(old);
|
|
}
|
|
|
|
void DesktopGridEffect::desktopsAdded(int old)
|
|
{
|
|
const int desktop = effects->numberOfDesktops();
|
|
for (int i = old; i <= effects->numberOfDesktops(); i++) {
|
|
// add a timeline for the new desktop
|
|
QTimeLine *newTimeline = new QTimeLine(zoomDuration, this);
|
|
newTimeline->setCurveShape(QTimeLine::EaseInOutCurve);
|
|
hoverTimeline.append(newTimeline);
|
|
}
|
|
|
|
// Create desktop name textures if enabled
|
|
if (desktopNameAlignment) {
|
|
QFont font;
|
|
font.setBold(true);
|
|
font.setPointSize(12);
|
|
for (int i = old; i < desktop; i++) {
|
|
EffectFrame* frame = effects->effectFrame(EffectFrameUnstyled, false);
|
|
frame->setFont(font);
|
|
frame->setText(effects->desktopName(i + 1));
|
|
frame->setAlignment(desktopNameAlignment);
|
|
desktopNames.append(frame);
|
|
}
|
|
}
|
|
|
|
if (isUsingPresentWindows()) {
|
|
for (int i = old+1; i <= effects->numberOfDesktops(); ++i) {
|
|
for (int j = 0; j < effects->numScreens(); ++j) {
|
|
WindowMotionManager manager;
|
|
foreach (EffectWindow * w, effects->stackingOrder()) {
|
|
if (w->isOnDesktop(i) && w->screen() == j &&isRelevantWithPresentWindows(w)) {
|
|
manager.manage(w);
|
|
}
|
|
}
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager);
|
|
m_managers.append(manager);
|
|
}
|
|
}
|
|
}
|
|
|
|
setupGrid();
|
|
|
|
// and repaint
|
|
effects->addRepaintFull();
|
|
}
|
|
|
|
void DesktopGridEffect::desktopsRemoved(int old)
|
|
{
|
|
const int desktop = effects->numberOfDesktops();
|
|
for (int i = desktop; i < old; i++) {
|
|
delete hoverTimeline.takeLast();
|
|
if (desktopNameAlignment) {
|
|
delete desktopNames.last();
|
|
desktopNames.removeLast();
|
|
}
|
|
if (isUsingPresentWindows()) {
|
|
for (int j = 0; j < effects->numScreens(); ++j) {
|
|
WindowMotionManager& manager = m_managers.last();
|
|
manager.unmanageAll();
|
|
m_managers.removeLast();
|
|
}
|
|
}
|
|
}
|
|
// add removed windows to the last desktop
|
|
if (isUsingPresentWindows()) {
|
|
for (int j = 0; j < effects->numScreens(); ++j) {
|
|
WindowMotionManager& manager = m_managers[(desktop-1)*(effects->numScreens())+j ];
|
|
foreach (EffectWindow * w, effects->stackingOrder()) {
|
|
if (manager.isManaging(w))
|
|
continue;
|
|
if (w->isOnDesktop(desktop) && w->screen() == j && isRelevantWithPresentWindows(w)) {
|
|
manager.manage(w);
|
|
}
|
|
}
|
|
m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager);
|
|
}
|
|
}
|
|
|
|
setupGrid();
|
|
|
|
// and repaint
|
|
effects->addRepaintFull();
|
|
}
|
|
//TODO: kill this function? or at least keep a consistent numeration with desktops starting from 1
|
|
QVector<int> DesktopGridEffect::desktopList(const EffectWindow *w) const
|
|
{
|
|
if (w->isOnAllDesktops()) {
|
|
static QVector<int> allDesktops;
|
|
if (allDesktops.count() != effects->numberOfDesktops()) {
|
|
allDesktops.resize(effects->numberOfDesktops());
|
|
for (int i = 0; i < effects->numberOfDesktops(); ++i)
|
|
allDesktops[i] = i;
|
|
}
|
|
return allDesktops;
|
|
}
|
|
|
|
QVector<int> desks;
|
|
desks.resize(w->desktops().count());
|
|
int i = 0;
|
|
for (const int desk : w->desktops()) {
|
|
desks[i++] = desk-1;
|
|
}
|
|
return desks;
|
|
}
|
|
|
|
bool DesktopGridEffect::isActive() const
|
|
{
|
|
return (timeline.currentValue() != 0 || activated || (isUsingPresentWindows() && isMotionManagerMovingWindows())) && !effects->isScreenLocked();
|
|
}
|
|
|
|
bool DesktopGridEffect::isRelevantWithPresentWindows(EffectWindow *w) const
|
|
{
|
|
if (w->isSpecialWindow() || w->isUtility()) {
|
|
return false;
|
|
}
|
|
|
|
if (w->isSkipSwitcher()) {
|
|
return false;
|
|
}
|
|
|
|
if (w->isDeleted()) {
|
|
return false;
|
|
}
|
|
|
|
if (!w->acceptsFocus()) {
|
|
return false;
|
|
}
|
|
|
|
if (!w->isCurrentTab()) {
|
|
return false;
|
|
}
|
|
|
|
if (!w->isOnCurrentActivity()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************
|
|
* DesktopButtonView
|
|
************************************************/
|
|
DesktopButtonsView::DesktopButtonsView(QWindow *parent)
|
|
: QQuickView(parent)
|
|
, effectWindow(nullptr)
|
|
, m_visible(false)
|
|
, m_posIsValid(false)
|
|
{
|
|
setFlags(Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
|
|
setColor(Qt::transparent);
|
|
|
|
rootContext()->setContextProperty(QStringLiteral("add"), QVariant(true));
|
|
rootContext()->setContextProperty(QStringLiteral("remove"), QVariant(true));
|
|
setSource(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/desktopgrid/main.qml"))));
|
|
if (QObject *item = rootObject()->findChild<QObject*>(QStringLiteral("addButton"))) {
|
|
connect(item, SIGNAL(clicked()), SIGNAL(addDesktop()));
|
|
}
|
|
if (QObject *item = rootObject()->findChild<QObject*>(QStringLiteral("removeButton"))) {
|
|
connect(item, SIGNAL(clicked()), SIGNAL(removeDesktop()));
|
|
}
|
|
}
|
|
|
|
void DesktopButtonsView::windowInputMouseEvent(QMouseEvent *e)
|
|
{
|
|
if (e->type() == QEvent::MouseMove) {
|
|
mouseMoveEvent(e);
|
|
} else if (e->type() == QEvent::MouseButtonPress) {
|
|
mousePressEvent(e);
|
|
} else if (e->type() == QEvent::MouseButtonDblClick) {
|
|
mouseDoubleClickEvent(e);
|
|
} else if (e->type() == QEvent::MouseButtonRelease) {
|
|
mouseReleaseEvent(e);
|
|
}
|
|
}
|
|
|
|
void DesktopButtonsView::setAddDesktopEnabled(bool enable)
|
|
{
|
|
rootContext()->setContextProperty(QStringLiteral("add"), QVariant(enable));
|
|
}
|
|
|
|
void DesktopButtonsView::setRemoveDesktopEnabled(bool enable)
|
|
{
|
|
rootContext()->setContextProperty(QStringLiteral("remove"), QVariant(enable));
|
|
}
|
|
|
|
bool DesktopButtonsView::isVisible() const
|
|
{
|
|
return m_visible;
|
|
}
|
|
|
|
void DesktopButtonsView::show()
|
|
{
|
|
if (!m_visible && m_posIsValid) {
|
|
setPosition(m_pos);
|
|
m_posIsValid = false;
|
|
}
|
|
m_visible = true;
|
|
QQuickView::show();
|
|
}
|
|
|
|
void DesktopButtonsView::hide()
|
|
{
|
|
if (!m_posIsValid) {
|
|
m_pos = position();
|
|
m_posIsValid = true;
|
|
setPosition(-width(), -height());
|
|
}
|
|
m_visible = false;
|
|
}
|
|
|
|
} // namespace
|
|
|