rework of slide effect internals
Fixed a bunch of bugs and polished the slide effect. Plugged the slide effect into the new VirtualDesktopManager interface desktopChanging() to allow for mac os style desktop switching. BUG: 448419 BUG: 401479
This commit is contained in:
parent
c7c1ac78ea
commit
3c557be707
2 changed files with 337 additions and 179 deletions
|
@ -15,6 +15,9 @@
|
|||
// KConfigSkeleton
|
||||
#include "slideconfig.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
|
@ -27,21 +30,27 @@ SlideEffect::SlideEffect()
|
|||
|
||||
connect(effects, QOverload<int, int, EffectWindow *>::of(&EffectsHandler::desktopChanged),
|
||||
this, &SlideEffect::desktopChanged);
|
||||
connect(effects, QOverload<uint, QPointF, EffectWindow *>::of(&EffectsHandler::desktopChanging),
|
||||
this, &SlideEffect::desktopChanging);
|
||||
connect(effects, QOverload<>::of(&EffectsHandler::desktopChangingCancelled),
|
||||
this, &SlideEffect::desktopChangingCancelled);
|
||||
connect(effects, &EffectsHandler::windowAdded,
|
||||
this, &SlideEffect::windowAdded);
|
||||
connect(effects, &EffectsHandler::windowDeleted,
|
||||
this, &SlideEffect::windowDeleted);
|
||||
connect(effects, &EffectsHandler::numberDesktopsChanged,
|
||||
this, &SlideEffect::stop);
|
||||
this, &SlideEffect::finishedSwitching);
|
||||
connect(effects, &EffectsHandler::screenAdded,
|
||||
this, &SlideEffect::stop);
|
||||
this, &SlideEffect::finishedSwitching);
|
||||
connect(effects, &EffectsHandler::screenRemoved,
|
||||
this, &SlideEffect::stop);
|
||||
this, &SlideEffect::finishedSwitching);
|
||||
|
||||
m_currentPosition = effects->desktopCoords(effects->currentDesktop());
|
||||
}
|
||||
|
||||
SlideEffect::~SlideEffect()
|
||||
{
|
||||
stop();
|
||||
finishedSwitching();
|
||||
}
|
||||
|
||||
bool SlideEffect::supported()
|
||||
|
@ -53,8 +62,8 @@ void SlideEffect::reconfigure(ReconfigureFlags)
|
|||
{
|
||||
SlideConfig::self()->read();
|
||||
|
||||
m_timeLine.setDuration(
|
||||
std::chrono::milliseconds(animationTime<SlideConfig>(500)));
|
||||
m_animationDuration = animationTime<SlideConfig>(500);
|
||||
m_timeLine.setDuration(std::chrono::milliseconds(m_animationDuration));
|
||||
|
||||
m_hGap = SlideConfig::horizontalGap();
|
||||
m_vGap = SlideConfig::verticalGap();
|
||||
|
@ -64,98 +73,69 @@ void SlideEffect::reconfigure(ReconfigureFlags)
|
|||
|
||||
void SlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
|
||||
{
|
||||
std::chrono::milliseconds delta = std::chrono::milliseconds::zero();
|
||||
if (m_lastPresentTime.count()) {
|
||||
delta = presentTime - m_lastPresentTime;
|
||||
}
|
||||
m_lastPresentTime = presentTime;
|
||||
std::chrono::milliseconds timeDelta = std::chrono::milliseconds::zero();
|
||||
if (m_lastPresentTime.count()) {
|
||||
timeDelta = presentTime - m_lastPresentTime;
|
||||
}
|
||||
m_lastPresentTime = presentTime;
|
||||
|
||||
m_timeLine.update(delta);
|
||||
m_timeLine.update(timeDelta);
|
||||
|
||||
data.mask |= PAINT_SCREEN_TRANSFORMED
|
||||
| PAINT_SCREEN_BACKGROUND_FIRST;
|
||||
if (!m_gestureActive) { // When animating
|
||||
m_currentPosition = m_startPos + (m_endPos - m_startPos) * m_timeLine.value();
|
||||
}
|
||||
|
||||
effects->prePaintScreen(data, presentTime);
|
||||
}
|
||||
data.mask |= PAINT_SCREEN_TRANSFORMED
|
||||
| PAINT_SCREEN_BACKGROUND_FIRST;
|
||||
|
||||
/**
|
||||
* Wrap vector @p diff around grid @p w x @p h.
|
||||
*
|
||||
* Wrapping is done in such a way that magnitude of x and y component of vector
|
||||
* @p diff is less than half of @p w and half of @p h, respectively. This will
|
||||
* result in having the "shortest" path between two points.
|
||||
*
|
||||
* @param diff Vector between two points
|
||||
* @param w Width of the desktop grid
|
||||
* @param h Height of the desktop grid
|
||||
*/
|
||||
inline void wrapDiff(QPoint &diff, int w, int h)
|
||||
{
|
||||
if (diff.x() > w/2) {
|
||||
diff.setX(diff.x() - w);
|
||||
} else if (diff.x() < -w/2) {
|
||||
diff.setX(diff.x() + w);
|
||||
}
|
||||
|
||||
if (diff.y() > h/2) {
|
||||
diff.setY(diff.y() - h);
|
||||
} else if (diff.y() < -h/2) {
|
||||
diff.setY(diff.y() + h);
|
||||
}
|
||||
}
|
||||
|
||||
inline QRegion buildClipRegion(const QPoint &pos, int w, int h)
|
||||
{
|
||||
const QSize screenSize = effects->virtualScreenSize();
|
||||
QRegion r = QRect(pos, screenSize);
|
||||
if (effects->optionRollOverDesktops()) {
|
||||
r |= (r & QRect(-w, 0, w, h)).translated(w, 0); // W
|
||||
r |= (r & QRect(w, 0, w, h)).translated(-w, 0); // E
|
||||
|
||||
r |= (r & QRect(0, -h, w, h)).translated(0, h); // N
|
||||
r |= (r & QRect(0, h, w, h)).translated(0, -h); // S
|
||||
|
||||
r |= (r & QRect(-w, -h, w, h)).translated(w, h); // NW
|
||||
r |= (r & QRect(w, -h, w, h)).translated(-w, h); // NE
|
||||
r |= (r & QRect(w, h, w, h)).translated(-w, -h); // SE
|
||||
r |= (r & QRect(-w, h, w, h)).translated(w, -h); // SW
|
||||
}
|
||||
return r;
|
||||
effects->prePaintScreen(data, presentTime);
|
||||
}
|
||||
|
||||
void SlideEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data)
|
||||
{
|
||||
const bool wrap = effects->optionRollOverDesktops();
|
||||
const int w = workspaceWidth();
|
||||
const int h = workspaceHeight();
|
||||
const int w = effects->desktopGridWidth();
|
||||
const int h = effects->desktopGridHeight();
|
||||
bool wrappingX = false, wrappingY = false;
|
||||
|
||||
QPoint currentPos = m_startPos + m_diff * m_timeLine.value();
|
||||
QPointF drawPosition = forcePositivePosition(m_currentPosition);
|
||||
|
||||
// When "Desktop navigation wraps around" checkbox is checked, currentPos
|
||||
// can be outside the rectangle Rect{x:-w, y:-h, width:2*w, height: 2*h},
|
||||
// so we map currentPos back to the rect.
|
||||
if (wrap) {
|
||||
currentPos.setX(currentPos.x() % w);
|
||||
currentPos.setY(currentPos.y() % h);
|
||||
if (wrap) { //
|
||||
drawPosition = constrainToDrawableRange(drawPosition);
|
||||
}
|
||||
|
||||
//Clipping
|
||||
QVector<int> visibleDesktops;
|
||||
visibleDesktops.reserve(4); // 4 - maximum number of visible desktops
|
||||
const QRegion clipRegion = buildClipRegion(currentPos, w, h);
|
||||
for (int i = 1; i <= effects->numberOfDesktops(); i++) {
|
||||
const QRect desktopGeo = desktopGeometry(i);
|
||||
if (!clipRegion.contains(desktopGeo)) {
|
||||
continue;
|
||||
if (effects->desktopGridCoords(i).x() % w == (int)(m_currentPosition.x()) % w) {
|
||||
visibleDesktops << i;
|
||||
} else if (effects->desktopGridCoords(i).x() % w == ((int)(m_currentPosition.x()) + 1) % w) {
|
||||
visibleDesktops << i;
|
||||
} else if (effects->desktopGridCoords(i).y() % h == (int)(m_currentPosition.y()) % h) {
|
||||
visibleDesktops << i;
|
||||
} else if (effects->desktopGridCoords(i).y() % h == ((int)(m_currentPosition.y()) + 1) % h) {
|
||||
visibleDesktops << i;
|
||||
}
|
||||
visibleDesktops << i;
|
||||
}
|
||||
|
||||
// When we enter a virtual desktop that has a window in fullscreen mode,
|
||||
// stacking order is fine. When we leave a virtual desktop that has
|
||||
// a window in fullscreen mode, stacking order is no longer valid
|
||||
// because panels are raised above the fullscreen window. Construct
|
||||
// a list of fullscreen windows, so we can decide later whether
|
||||
// docks should be visible on different virtual desktops.
|
||||
//If we're wrapping, draw the desktop in the second position.
|
||||
if (drawPosition.x() > w - 1) {
|
||||
wrappingX = true;
|
||||
}
|
||||
|
||||
if (drawPosition.y() > h - 1) {
|
||||
wrappingY = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we enter a virtual desktop that has a window in fullscreen mode,
|
||||
* stacking order is fine. When we leave a virtual desktop that has
|
||||
* a window in fullscreen mode, stacking order is no longer valid
|
||||
* because panels are raised above the fullscreen window. Construct
|
||||
* a list of fullscreen windows, so we can decide later whether
|
||||
* docks should be visible on different virtual desktops.
|
||||
*/
|
||||
if (m_slideDocks) {
|
||||
const auto windows = effects->stackingOrder();
|
||||
m_paintCtx.fullscreenWindows.clear();
|
||||
|
@ -167,25 +147,41 @@ void SlideEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &
|
|||
}
|
||||
}
|
||||
|
||||
// Screen is painted in several passes. Each painting pass paints
|
||||
// a single virtual desktop. There could be either 2 or 4 painting
|
||||
// passes, depending how an user moves between virtual desktops.
|
||||
// Windows, such as docks or keep-above windows, are painted in
|
||||
// the last pass so they are above other windows.
|
||||
/*
|
||||
* Screen is painted in several passes. Each painting pass paints
|
||||
* a single virtual desktop. There could be either 2 or 4 painting
|
||||
* passes, depending how an user moves between virtual desktops.
|
||||
* Windows, such as docks or keep-above windows, are painted in
|
||||
* the last pass so they are above other windows.
|
||||
*/
|
||||
m_paintCtx.firstPass = true;
|
||||
const int lastDesktop = visibleDesktops.last();
|
||||
for (int desktop : qAsConst(visibleDesktops)) {
|
||||
m_paintCtx.desktop = desktop;
|
||||
m_paintCtx.lastPass = (lastDesktop == desktop);
|
||||
m_paintCtx.translation = desktopCoords(desktop) - currentPos;
|
||||
if (wrap) {
|
||||
wrapDiff(m_paintCtx.translation, w, h);
|
||||
m_paintCtx.translation = QPointF(effects->desktopGridCoords(desktop)) - drawPosition;//TODO: verify
|
||||
|
||||
// Decide if that first desktop should be drawn at 0 or the higher position used for wrapping.
|
||||
if (effects->desktopGridCoords(desktop).x() == 0 && wrappingX) {
|
||||
m_paintCtx.translation = QPointF(m_paintCtx.translation.x() + w, m_paintCtx.translation.y());
|
||||
}
|
||||
|
||||
if (effects->desktopGridCoords(desktop).y() == 0 && wrappingY) {
|
||||
m_paintCtx.translation = QPointF(m_paintCtx.translation.x(), m_paintCtx.translation.y() + h);
|
||||
}
|
||||
|
||||
effects->paintScreen(mask, region, data);
|
||||
m_paintCtx.firstPass = false;
|
||||
}
|
||||
}
|
||||
|
||||
QPoint SlideEffect::getDrawCoords(QPointF pos, EffectScreen *screen){
|
||||
QPoint c = QPoint();
|
||||
c.setX(pos.x() * (screen->geometry().width() + m_hGap));
|
||||
c.setY(pos.y() * (screen->geometry().height() + m_vGap));
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether given window @p w should be transformed/translated.
|
||||
* @returns @c true if given window @p w should be transformed, otherwise @c false
|
||||
|
@ -228,15 +224,19 @@ bool SlideEffect::isPainted(const EffectWindow *w) const
|
|||
return true;
|
||||
}
|
||||
if (w->isDesktop()) {
|
||||
// If desktop background is not being slided, draw it only
|
||||
// in the first pass. Otherwise, desktop backgrounds from
|
||||
// follow-up virtual desktops will be drawn above windows
|
||||
// from previous virtual desktops.
|
||||
/*
|
||||
* If desktop background is not being slided, draw it only
|
||||
* in the first pass. Otherwise, desktop backgrounds from
|
||||
* follow-up virtual desktops will be drawn above windows
|
||||
* from previous virtual desktops.
|
||||
*/
|
||||
return m_slideBackground || m_paintCtx.firstPass;
|
||||
}
|
||||
// In order to make sure that 'keep above' windows are above
|
||||
// other windows during transition to another virtual desktop,
|
||||
// they should be painted in the last pass.
|
||||
/*
|
||||
* In order to make sure that 'keep above' windowscreen->geometry().x()s are above
|
||||
* other windows during transition to another virtual desktop,
|
||||
* they should be painted in the last pass.
|
||||
*/
|
||||
if (w->keepAbove()) {
|
||||
return m_paintCtx.lastPass;
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ bool SlideEffect::isPainted(const EffectWindow *w) const
|
|||
void SlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
|
||||
{
|
||||
const bool painted = isPainted(w);
|
||||
|
||||
if (painted) {
|
||||
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
|
||||
} else {
|
||||
|
@ -265,96 +266,74 @@ void SlideEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std:
|
|||
|
||||
void SlideEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
|
||||
{
|
||||
if (isTranslated(w)) {
|
||||
data += m_paintCtx.translation;
|
||||
for (EffectScreen *screen: effects->screens()) {
|
||||
QPoint translation = getDrawCoords(m_paintCtx.translation, screen);
|
||||
if (isTranslated(w)) {
|
||||
data += translation;
|
||||
}
|
||||
|
||||
effects->paintWindow(
|
||||
w,
|
||||
mask,
|
||||
// Only paint the region that intersects the current screen and desktop.
|
||||
region.intersected(effects->clientArea(ScreenArea, w)).intersected(effects->clientArea(ScreenArea, screen, effects->currentDesktop())),
|
||||
data);
|
||||
|
||||
if (isTranslated(w)) {
|
||||
// Undo the translation for the next screen. I know, it hurts me too.
|
||||
data += QPoint(-translation.x(), -translation.y());
|
||||
}
|
||||
}
|
||||
effects->paintWindow(w, mask, region, data);
|
||||
}
|
||||
|
||||
void SlideEffect::postPaintScreen()
|
||||
{
|
||||
if (m_timeLine.done()) {
|
||||
stop();
|
||||
if (m_timeLine.done() && !m_gestureActive) {
|
||||
finishedSwitching();
|
||||
}
|
||||
|
||||
effects->addRepaintFull();
|
||||
effects->postPaintScreen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of the top-left corner of desktop @p id within desktop grid with gaps.
|
||||
* @param id ID of a virtual desktop
|
||||
/*
|
||||
* Negative desktop positions aren't allowed.
|
||||
*/
|
||||
QPoint SlideEffect::desktopCoords(int id) const
|
||||
QPointF SlideEffect::forcePositivePosition(QPointF p) const
|
||||
{
|
||||
QPoint c = effects->desktopCoords(id);
|
||||
const QPoint gridPos = effects->desktopGridCoords(id);
|
||||
c.setX(c.x() + m_hGap * gridPos.x());
|
||||
c.setY(c.y() + m_vGap * gridPos.y());
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geometry of desktop @p id within desktop grid with gaps.
|
||||
* @param id ID of a virtual desktop
|
||||
*/
|
||||
QRect SlideEffect::desktopGeometry(int id) const
|
||||
{
|
||||
QRect g = effects->virtualScreenGeometry();
|
||||
g.translate(desktopCoords(id));
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get width of a virtual desktop grid.
|
||||
*/
|
||||
int SlideEffect::workspaceWidth() const
|
||||
{
|
||||
int w = effects->workspaceWidth();
|
||||
w += m_hGap * effects->desktopGridWidth();
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get height of a virtual desktop grid.
|
||||
*/
|
||||
int SlideEffect::workspaceHeight() const
|
||||
{
|
||||
int h = effects->workspaceHeight();
|
||||
h += m_vGap * effects->desktopGridHeight();
|
||||
return h;
|
||||
while (p.x() < 0) {
|
||||
p.setX(p.x() + effects->desktopGridWidth());
|
||||
}
|
||||
while (p.y() < 0) {
|
||||
p.setY(p.y() + effects->desktopGridHeight());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool SlideEffect::shouldElevate(const EffectWindow *w) const
|
||||
{
|
||||
// Static docks(i.e. this effect doesn't slide docks) should be elevated
|
||||
// so they can properly animate themselves when an user enters or leaves
|
||||
// a virtual desktop with a window in fullscreen mode.
|
||||
/*
|
||||
* Static docks(i.e. this effect doesn't slide docks) should be elevated
|
||||
* so they can properly animate themselves when an user enters or leaves
|
||||
* a virtual desktop with a window in fullscreen mode.
|
||||
*/
|
||||
return w->isDock() && !m_slideDocks;
|
||||
}
|
||||
|
||||
void SlideEffect::start(int old, int current, EffectWindow *movingWindow)
|
||||
/*
|
||||
* This function is called when the desktop changes.
|
||||
* Called AFTER the gesture is released.
|
||||
* Sets up animation to round off to the new current desktop.
|
||||
*/
|
||||
void SlideEffect::startAnimation(int old, int current, EffectWindow *movingWindow)
|
||||
{
|
||||
Q_UNUSED(old)
|
||||
|
||||
m_movingWindow = movingWindow;
|
||||
|
||||
const bool wrap = effects->optionRollOverDesktops();
|
||||
const int w = workspaceWidth();
|
||||
const int h = workspaceHeight();
|
||||
|
||||
if (m_active) {
|
||||
QPoint passed = m_diff * m_timeLine.value();
|
||||
QPoint currentPos = m_startPos + passed;
|
||||
QPoint delta = desktopCoords(current) - desktopCoords(old);
|
||||
if (wrap) {
|
||||
wrapDiff(delta, w, h);
|
||||
}
|
||||
m_diff += delta - passed;
|
||||
m_startPos = currentPos;
|
||||
// TODO: Figure out how to smooth movement.
|
||||
m_timeLine.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
//Handle stacking order
|
||||
const auto windows = effects->stackingOrder();
|
||||
for (EffectWindow *w : windows) {
|
||||
if (shouldElevate(w)) {
|
||||
|
@ -365,18 +344,35 @@ void SlideEffect::start(int old, int current, EffectWindow *movingWindow)
|
|||
w->setData(WindowForceBlurRole, QVariant(true));
|
||||
}
|
||||
|
||||
m_diff = desktopCoords(current) - desktopCoords(old);
|
||||
if (wrap) {
|
||||
wrapDiff(m_diff, w, h);
|
||||
}
|
||||
m_startPos = desktopCoords(old);
|
||||
m_timeLine.reset();
|
||||
// Set up animation
|
||||
m_active = true;
|
||||
m_timeLine.reset();
|
||||
|
||||
m_startPos = m_currentPosition;
|
||||
m_endPos = effects->desktopGridCoords(current);
|
||||
if (wrap) {
|
||||
optimizePath();
|
||||
}
|
||||
|
||||
// Find an apropriate duration
|
||||
m_timeLine.setDuration(std::chrono::milliseconds(m_animationDuration));
|
||||
|
||||
QPointF distance = m_startPos - m_endPos;
|
||||
distance.setX(std::abs(distance.x()));
|
||||
distance.setY(std::abs(distance.y()));
|
||||
if (distance.x() < 1 && distance.y() < 1) {
|
||||
if (distance.x() > distance.y()) {
|
||||
m_timeLine.setDuration(std::chrono::milliseconds((int)(m_animationDuration * distance.x())));
|
||||
} else {
|
||||
m_timeLine.setDuration(std::chrono::milliseconds((int)(m_animationDuration * distance.y())));
|
||||
}
|
||||
}
|
||||
|
||||
effects->setActiveFullScreenEffect(this);
|
||||
effects->addRepaintFull();
|
||||
}
|
||||
|
||||
void SlideEffect::stop()
|
||||
void SlideEffect::finishedSwitching()
|
||||
{
|
||||
if (!m_active) {
|
||||
return;
|
||||
|
@ -397,14 +393,70 @@ void SlideEffect::stop()
|
|||
m_active = false;
|
||||
m_lastPresentTime = std::chrono::milliseconds::zero();
|
||||
effects->setActiveFullScreenEffect(nullptr);
|
||||
m_currentPosition = effects->desktopGridCoords(effects->currentDesktop());
|
||||
}
|
||||
|
||||
void SlideEffect::desktopChanged(int old, int current, EffectWindow *with)
|
||||
{
|
||||
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) {
|
||||
if (effects->hasActiveFullScreenEffect() && effects->activeFullScreenEffect() != this) {
|
||||
m_currentPosition = effects->desktopGridCoords(effects->currentDesktop());
|
||||
return;
|
||||
}
|
||||
start(old, current, with);
|
||||
|
||||
m_gestureActive = false;
|
||||
startAnimation(old, current, with);
|
||||
}
|
||||
|
||||
void SlideEffect::desktopChanging(uint old, QPointF desktopOffset, EffectWindow *with)
|
||||
{
|
||||
if (effects->hasActiveFullScreenEffect() && effects->activeFullScreenEffect() != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_gestureActive = true;
|
||||
m_movingWindow = with;
|
||||
|
||||
const bool wrap = effects->optionRollOverDesktops();
|
||||
|
||||
// Find desktop position based on animationDelta
|
||||
QPoint gridPos = effects->desktopGridCoords(old);
|
||||
m_currentPosition.setX(gridPos.x() + desktopOffset.x());
|
||||
m_currentPosition.setY(gridPos.y() + desktopOffset.y());
|
||||
|
||||
m_currentPosition = forcePositivePosition(m_currentPosition);
|
||||
|
||||
if (!wrap) {
|
||||
m_currentPosition = moveInsideDesktopGrid(m_currentPosition);
|
||||
}
|
||||
|
||||
m_active = true;
|
||||
effects->setActiveFullScreenEffect(this);
|
||||
effects->addRepaintFull();
|
||||
}
|
||||
|
||||
void SlideEffect::desktopChangingCancelled()
|
||||
{
|
||||
if (effects->hasActiveFullScreenEffect() && effects->activeFullScreenEffect() != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "Cancelled" << std::endl;
|
||||
m_gestureActive = false;
|
||||
startAnimation(effects->currentDesktop(), effects->currentDesktop(), nullptr);
|
||||
}
|
||||
|
||||
QPointF SlideEffect::moveInsideDesktopGrid(QPointF p)
|
||||
{
|
||||
if (p.x() < 0) {
|
||||
p.setX(0);
|
||||
} else if (p.y() < 0) {
|
||||
p.setY(0);
|
||||
} else if (p.x() > effects->desktopGridWidth() - 1) {
|
||||
p.setX(effects->desktopGridWidth() - 1);
|
||||
} else if (p.y() > effects->desktopGridHeight() - 1) {
|
||||
p.setY(effects->desktopGridHeight() - 1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void SlideEffect::windowAdded(EffectWindow *w)
|
||||
|
@ -432,4 +484,76 @@ void SlideEffect::windowDeleted(EffectWindow *w)
|
|||
m_paintCtx.fullscreenWindows.removeAll(w);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the fastest path between two desktops.
|
||||
* This function decides when it's better to wrap around the grid or not.
|
||||
* Only call if wrapping is enabled.
|
||||
*/
|
||||
void SlideEffect::optimizePath()
|
||||
{
|
||||
int w = effects->desktopGridWidth();
|
||||
int h = effects->desktopGridHeight();
|
||||
|
||||
// Keep coordinates as low as possible
|
||||
if(m_startPos.x() >= w && m_endPos.x() >= w) {
|
||||
m_startPos.setX(fmod(m_startPos.x(), w));
|
||||
m_endPos.setX(fmod(m_endPos.x(), w));
|
||||
}
|
||||
if(m_startPos.y() >= h && m_endPos.y() >= h) {
|
||||
m_startPos.setY(fmod(m_startPos.y(), h));
|
||||
m_endPos.setY(fmod(m_endPos.y(), h));
|
||||
}
|
||||
|
||||
/*
|
||||
* Is there is a shorter possible route?
|
||||
* If the x distance to be traveled is more than half the grid width, it's faster to wrap.
|
||||
* To avoid negative coordinates, take the lower coordinate and raise.
|
||||
*/
|
||||
if (std::abs((m_startPos.x() - m_endPos.x())) > (double)w / (double)2) {
|
||||
if (m_startPos.x() < m_endPos.x()) {
|
||||
while (m_startPos.x() < m_endPos.x())
|
||||
m_startPos.setX(m_startPos.x() + w);
|
||||
|
||||
} else {
|
||||
while (m_endPos.x() < m_startPos.x())
|
||||
m_endPos.setX(m_endPos.x() + w);
|
||||
}
|
||||
// Keep coordinates as low as possible
|
||||
if(m_startPos.x() >= w && m_endPos.x() >= w) {
|
||||
m_startPos.setX(fmod(m_startPos.x(), w));
|
||||
m_endPos.setX(fmod(m_endPos.x(), w));
|
||||
}
|
||||
}
|
||||
|
||||
// Same for y
|
||||
if (std::abs((m_endPos.y() - m_startPos.y())) > (double)h / (double)2) {
|
||||
if (m_startPos.y() < m_endPos.y()) {
|
||||
while (m_startPos.y() < m_endPos.y())
|
||||
m_startPos.setY(m_startPos.y() + h);
|
||||
|
||||
} else {
|
||||
while (m_endPos.y() < m_startPos.y())
|
||||
m_endPos.setY(m_endPos.y() + h);
|
||||
}
|
||||
// Keep coordinates as low as possible
|
||||
if(m_startPos.y() >= h && m_endPos.y() >= h) {
|
||||
m_startPos.setY(fmod(m_startPos.y(), h));
|
||||
m_endPos.setY(fmod(m_endPos.y(), h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes the point and uses modulus to keep draw position within [0, desktopGridWidth]
|
||||
* The render loop will draw the first desktop (0) after the last one (at position desktopGridWidth) for the wrap animation.
|
||||
* This function finds the true fastest path, regardless of which direction the animation is already going;
|
||||
* I was a little upset about this limitation until I realized that MacOS can't even wrap desktops :)
|
||||
*/
|
||||
QPointF SlideEffect::constrainToDrawableRange(QPointF p)
|
||||
{
|
||||
p.setX(fmod(p.x(), effects->desktopGridWidth()));
|
||||
p.setY(fmod(p.y(), effects->desktopGridHeight()));
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -18,6 +18,31 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
/*
|
||||
* How it Works:
|
||||
*
|
||||
* This effect doesn't change the current desktop, only recieves changes from the VirtualDesktopManager.
|
||||
* The only visually aparent inputs are desktopChanged() and desktopChanging().
|
||||
*
|
||||
* When responding to desktopChanging(), the draw position is only affected by what's recieved from there.
|
||||
* After desktopChanging() is done, or without desktopChanging() having been called at all, desktopChanged() is called.
|
||||
* The desktopChanged() function configures the m_startPos and m_endPos for the animation, and the duration.
|
||||
*
|
||||
* m_currentPosition and m_paintCtx.translation and everything else not labeled "drawCoordinate" uses desktops as a unit.
|
||||
* Exmp: 1.2 means the dekstop at index 1 shifted over by .2 desktops.
|
||||
* All coords must be positive.
|
||||
*
|
||||
* For the wrapping effect, the render loop has to handle desktop coordinates larger than the total grid's width.
|
||||
* 1. It uses modulus to keep the desktop coords in the range [0, gridWidth].
|
||||
* 2. It will draw the desktop at index 0 at index gridWidth if it has to.
|
||||
* I will not draw any thing farther outside the range than that.
|
||||
*
|
||||
* I've put an explanation of all the important private vars down at the bottom.
|
||||
*
|
||||
* Good luck :)
|
||||
*/
|
||||
|
||||
|
||||
class SlideEffect : public Effect
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -58,40 +83,49 @@ public:
|
|||
|
||||
private Q_SLOTS:
|
||||
void desktopChanged(int old, int current, EffectWindow *with);
|
||||
void desktopChanging(uint old, QPointF desktopOffset, EffectWindow* with);
|
||||
void desktopChangingCancelled();
|
||||
void windowAdded(EffectWindow *w);
|
||||
void windowDeleted(EffectWindow *w);
|
||||
|
||||
private:
|
||||
QPoint desktopCoords(int id) const;
|
||||
QRect desktopGeometry(int id) const;
|
||||
int workspaceWidth() const;
|
||||
int workspaceHeight() const;
|
||||
|
||||
QPoint getDrawCoords(QPointF pos, EffectScreen *screen);
|
||||
bool isTranslated(const EffectWindow *w) const;
|
||||
bool isPainted(const EffectWindow *w) const;
|
||||
bool shouldElevate(const EffectWindow *w) const;
|
||||
QPointF moveInsideDesktopGrid(QPointF p);
|
||||
QPointF constrainToDrawableRange(QPointF p);
|
||||
QPointF forcePositivePosition(QPointF p) const;
|
||||
void optimizePath(); //Find the best path to target desktop
|
||||
|
||||
void start(int old, int current, EffectWindow *movingWindow = nullptr);
|
||||
void stop();
|
||||
void startAnimation(int old, int current, EffectWindow *movingWindow = nullptr);
|
||||
void finishedSwitching();
|
||||
|
||||
private:
|
||||
int m_hGap;
|
||||
int m_vGap;
|
||||
bool m_slideDocks;
|
||||
bool m_slideBackground;
|
||||
int m_animationDuration; // Miliseconds for 1 complete desktop switch
|
||||
|
||||
bool m_active = false;
|
||||
TimeLine m_timeLine;
|
||||
QPoint m_startPos;
|
||||
QPoint m_diff;
|
||||
|
||||
// When the desktop isn't desktopChanging(), these two variables are used to control the animation path.
|
||||
// They use desktops as a unit.
|
||||
QPointF m_startPos;
|
||||
QPointF m_endPos;
|
||||
|
||||
EffectWindow *m_movingWindow = nullptr;
|
||||
std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero();
|
||||
bool m_gestureActive = false; // If we're currently animating a gesture
|
||||
QPointF m_currentPosition; // Should always be kept up to date with where on the grid we're seeing.
|
||||
|
||||
struct {
|
||||
int desktop;
|
||||
bool firstPass;
|
||||
bool lastPass;
|
||||
QPoint translation;
|
||||
QPointF translation; //Uses desktops as units
|
||||
|
||||
EffectWindowList fullscreenWindows;
|
||||
} m_paintCtx;
|
||||
|
@ -101,7 +135,7 @@ private:
|
|||
|
||||
inline int SlideEffect::duration() const
|
||||
{
|
||||
return m_timeLine.duration().count();
|
||||
return m_animationDuration;
|
||||
}
|
||||
|
||||
inline int SlideEffect::horizontalGap() const
|
||||
|
|
Loading…
Reference in a new issue