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
26a4f75944
commit
003c820e00
3 changed files with 316 additions and 138 deletions
|
@ -15,6 +15,8 @@
|
||||||
// KConfigSkeleton
|
// KConfigSkeleton
|
||||||
#include "slideconfig.h"
|
#include "slideconfig.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -27,21 +29,27 @@ SlideEffect::SlideEffect()
|
||||||
|
|
||||||
connect(effects, QOverload<int, int, EffectWindow *>::of(&EffectsHandler::desktopChanged),
|
connect(effects, QOverload<int, int, EffectWindow *>::of(&EffectsHandler::desktopChanged),
|
||||||
this, &SlideEffect::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,
|
connect(effects, &EffectsHandler::windowAdded,
|
||||||
this, &SlideEffect::windowAdded);
|
this, &SlideEffect::windowAdded);
|
||||||
connect(effects, &EffectsHandler::windowDeleted,
|
connect(effects, &EffectsHandler::windowDeleted,
|
||||||
this, &SlideEffect::windowDeleted);
|
this, &SlideEffect::windowDeleted);
|
||||||
connect(effects, &EffectsHandler::numberDesktopsChanged,
|
connect(effects, &EffectsHandler::numberDesktopsChanged,
|
||||||
this, &SlideEffect::stop);
|
this, &SlideEffect::finishedSwitching);
|
||||||
connect(effects, &EffectsHandler::screenAdded,
|
connect(effects, &EffectsHandler::screenAdded,
|
||||||
this, &SlideEffect::stop);
|
this, &SlideEffect::finishedSwitching);
|
||||||
connect(effects, &EffectsHandler::screenRemoved,
|
connect(effects, &EffectsHandler::screenRemoved,
|
||||||
this, &SlideEffect::stop);
|
this, &SlideEffect::finishedSwitching);
|
||||||
|
|
||||||
|
m_currentPosition = effects->desktopCoords(effects->currentDesktop());
|
||||||
}
|
}
|
||||||
|
|
||||||
SlideEffect::~SlideEffect()
|
SlideEffect::~SlideEffect()
|
||||||
{
|
{
|
||||||
stop();
|
finishedSwitching();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SlideEffect::supported()
|
bool SlideEffect::supported()
|
||||||
|
@ -53,8 +61,8 @@ void SlideEffect::reconfigure(ReconfigureFlags)
|
||||||
{
|
{
|
||||||
SlideConfig::self()->read();
|
SlideConfig::self()->read();
|
||||||
|
|
||||||
m_timeLine.setDuration(
|
m_fullAnimationDuration = animationTime<SlideConfig>(500);
|
||||||
std::chrono::milliseconds(animationTime<SlideConfig>(500)));
|
m_timeLine.setDuration(std::chrono::milliseconds(m_fullAnimationDuration));
|
||||||
|
|
||||||
m_hGap = SlideConfig::horizontalGap();
|
m_hGap = SlideConfig::horizontalGap();
|
||||||
m_vGap = SlideConfig::verticalGap();
|
m_vGap = SlideConfig::verticalGap();
|
||||||
|
@ -83,33 +91,39 @@ inline QRegion buildClipRegion(const QPoint &pos, int w, int h)
|
||||||
|
|
||||||
void SlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
|
void SlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
|
||||||
{
|
{
|
||||||
std::chrono::milliseconds delta = std::chrono::milliseconds::zero();
|
std::chrono::milliseconds timeDelta = std::chrono::milliseconds::zero();
|
||||||
if (m_lastPresentTime.count()) {
|
if (m_lastPresentTime.count()) {
|
||||||
delta = presentTime - m_lastPresentTime;
|
timeDelta = presentTime - m_lastPresentTime;
|
||||||
}
|
}
|
||||||
m_lastPresentTime = presentTime;
|
m_lastPresentTime = presentTime;
|
||||||
m_timeLine.update(delta);
|
m_timeLine.update(timeDelta);
|
||||||
|
|
||||||
const int w = workspaceWidth();
|
if (!m_gestureActive) { // When animating
|
||||||
const int h = workspaceHeight();
|
m_currentPosition = m_startPos + (m_endPos - m_startPos) * m_timeLine.value();
|
||||||
|
|
||||||
// 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.
|
|
||||||
m_paintCtx.currentPos = m_startPos + m_diff * m_timeLine.value();
|
|
||||||
if (effects->optionRollOverDesktops()) {
|
|
||||||
m_paintCtx.currentPos.setX(m_paintCtx.currentPos.x() % w);
|
|
||||||
m_paintCtx.currentPos.setY(m_paintCtx.currentPos.y() % h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int w = effects->desktopGridWidth();
|
||||||
|
const int h = effects->desktopGridHeight();
|
||||||
|
|
||||||
|
//Clipping
|
||||||
m_paintCtx.visibleDesktops.clear();
|
m_paintCtx.visibleDesktops.clear();
|
||||||
const QRegion clipRegion = buildClipRegion(m_paintCtx.currentPos, w, h);
|
m_paintCtx.visibleDesktops.reserve(4); // 4 - maximum number of visible desktops
|
||||||
|
bool includedX = false, includedY = false;
|
||||||
for (int i = 1; i <= effects->numberOfDesktops(); i++) {
|
for (int i = 1; i <= effects->numberOfDesktops(); i++) {
|
||||||
const QRect desktopGeo = desktopGeometry(i);
|
if (effects->desktopGridCoords(i).x() % w == (int)(m_currentPosition.x()) % w) {
|
||||||
if (!clipRegion.contains(desktopGeo)) {
|
includedX = true;
|
||||||
continue;
|
} else if (effects->desktopGridCoords(i).x() % w == ((int)(m_currentPosition.x()) + 1) % w) {
|
||||||
|
includedX = true;
|
||||||
|
}
|
||||||
|
if (effects->desktopGridCoords(i).y() % h == (int)(m_currentPosition.y()) % h) {
|
||||||
|
includedY = true;
|
||||||
|
} else if (effects->desktopGridCoords(i).y() % h == ((int)(m_currentPosition.y()) + 1) % h) {
|
||||||
|
includedY = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includedX && includedY) {
|
||||||
|
m_paintCtx.visibleDesktops << i;
|
||||||
}
|
}
|
||||||
m_paintCtx.visibleDesktops << i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
|
data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
|
||||||
|
@ -117,37 +131,28 @@ void SlideEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::millisec
|
||||||
effects->prePaintScreen(data, presentTime);
|
effects->prePaintScreen(data, presentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SlideEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data)
|
void SlideEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data)
|
||||||
{
|
{
|
||||||
const bool wrap = effects->optionRollOverDesktops();
|
const bool wrap = effects->optionRollOverDesktops();
|
||||||
const int w = workspaceWidth();
|
const int w = effects->desktopGridWidth();
|
||||||
const int h = workspaceHeight();
|
const int h = effects->desktopGridHeight();
|
||||||
|
bool wrappingX = false, wrappingY = false;
|
||||||
|
|
||||||
|
QPointF drawPosition = forcePositivePosition(m_currentPosition);
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
drawPosition = constrainToDrawableRange(drawPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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,
|
// 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
|
// stacking order is fine. When we leave a virtual desktop that has
|
||||||
|
@ -176,15 +181,30 @@ void SlideEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &
|
||||||
for (int desktop : qAsConst(m_paintCtx.visibleDesktops)) {
|
for (int desktop : qAsConst(m_paintCtx.visibleDesktops)) {
|
||||||
m_paintCtx.desktop = desktop;
|
m_paintCtx.desktop = desktop;
|
||||||
m_paintCtx.lastPass = (lastDesktop == desktop);
|
m_paintCtx.lastPass = (lastDesktop == desktop);
|
||||||
m_paintCtx.translation = desktopCoords(desktop) - m_paintCtx.currentPos;
|
m_paintCtx.translation = QPointF(effects->desktopGridCoords(desktop)) - drawPosition;//TODO: verify
|
||||||
if (wrap) {
|
|
||||||
wrapDiff(m_paintCtx.translation, w, h);
|
// 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);
|
effects->paintScreen(mask, region, data);
|
||||||
m_paintCtx.firstPass = false;
|
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.
|
* Decide whether given window @p w should be transformed/translated.
|
||||||
* @returns @c true if given window @p w should be transformed, otherwise @c false
|
* @returns @c true if given window @p w should be transformed, otherwise @c false
|
||||||
|
@ -264,64 +284,49 @@ void SlideEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowP
|
||||||
if (!isPainted(w)) {
|
if (!isPainted(w)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
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()
|
void SlideEffect::postPaintScreen()
|
||||||
{
|
{
|
||||||
if (m_timeLine.done()) {
|
if (m_timeLine.done() && !m_gestureActive) {
|
||||||
stop();
|
finishedSwitching();
|
||||||
}
|
}
|
||||||
|
|
||||||
effects->addRepaintFull();
|
effects->addRepaintFull();
|
||||||
effects->postPaintScreen();
|
effects->postPaintScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Get position of the top-left corner of desktop @p id within desktop grid with gaps.
|
* Negative desktop positions aren't allowed.
|
||||||
* @param id ID of a virtual desktop
|
|
||||||
*/
|
*/
|
||||||
QPoint SlideEffect::desktopCoords(int id) const
|
QPointF SlideEffect::forcePositivePosition(QPointF p) const
|
||||||
{
|
{
|
||||||
QPoint c = effects->desktopCoords(id);
|
if (p.x() < 0) {
|
||||||
const QPoint gridPos = effects->desktopGridCoords(id);
|
p.setX(p.x() + std::ceil(-p.x() / effects->desktopGridWidth()) * effects->desktopGridWidth());
|
||||||
c.setX(c.x() + m_hGap * gridPos.x());
|
}
|
||||||
c.setY(c.y() + m_vGap * gridPos.y());
|
if (p.y() < 0) {
|
||||||
return c;
|
p.setY(p.y() + std::ceil(-p.y() / effects->desktopGridHeight()) * effects->desktopGridHeight());
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SlideEffect::shouldElevate(const EffectWindow *w) const
|
bool SlideEffect::shouldElevate(const EffectWindow *w) const
|
||||||
|
@ -332,28 +337,20 @@ bool SlideEffect::shouldElevate(const EffectWindow *w) const
|
||||||
return w->isDock() && !m_slideDocks;
|
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;
|
m_movingWindow = movingWindow;
|
||||||
|
|
||||||
const bool wrap = effects->optionRollOverDesktops();
|
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();
|
const auto windows = effects->stackingOrder();
|
||||||
for (EffectWindow *w : windows) {
|
for (EffectWindow *w : windows) {
|
||||||
if (shouldElevate(w)) {
|
if (shouldElevate(w)) {
|
||||||
|
@ -364,18 +361,35 @@ void SlideEffect::start(int old, int current, EffectWindow *movingWindow)
|
||||||
w->setData(WindowForceBlurRole, QVariant(true));
|
w->setData(WindowForceBlurRole, QVariant(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_diff = desktopCoords(current) - desktopCoords(old);
|
// Set up animation
|
||||||
if (wrap) {
|
|
||||||
wrapDiff(m_diff, w, h);
|
|
||||||
}
|
|
||||||
m_startPos = desktopCoords(old);
|
|
||||||
m_timeLine.reset();
|
|
||||||
m_active = true;
|
m_active = true;
|
||||||
|
m_timeLine.reset();
|
||||||
|
|
||||||
|
m_startPos = m_currentPosition;
|
||||||
|
m_endPos = effects->desktopGridCoords(current);
|
||||||
|
if (wrap) {
|
||||||
|
optimizePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an apropriate duration
|
||||||
|
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(std::max(1, (int)(m_fullAnimationDuration * distance.x()))));
|
||||||
|
} else {
|
||||||
|
m_timeLine.setDuration(std::chrono::milliseconds(std::max(1, (int)(m_fullAnimationDuration * distance.y()))));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_timeLine.setDuration(std::chrono::milliseconds(m_fullAnimationDuration));
|
||||||
|
}
|
||||||
|
|
||||||
effects->setActiveFullScreenEffect(this);
|
effects->setActiveFullScreenEffect(this);
|
||||||
effects->addRepaintFull();
|
effects->addRepaintFull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideEffect::stop()
|
void SlideEffect::finishedSwitching()
|
||||||
{
|
{
|
||||||
if (!m_active) {
|
if (!m_active) {
|
||||||
return;
|
return;
|
||||||
|
@ -396,14 +410,72 @@ void SlideEffect::stop()
|
||||||
m_active = false;
|
m_active = false;
|
||||||
m_lastPresentTime = std::chrono::milliseconds::zero();
|
m_lastPresentTime = std::chrono::milliseconds::zero();
|
||||||
effects->setActiveFullScreenEffect(nullptr);
|
effects->setActiveFullScreenEffect(nullptr);
|
||||||
|
m_currentPosition = effects->desktopGridCoords(effects->currentDesktop());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideEffect::desktopChanged(int old, int current, EffectWindow *with)
|
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;
|
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());
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
m_currentPosition = forcePositivePosition(m_currentPosition);
|
||||||
|
} else {
|
||||||
|
m_currentPosition = moveInsideDesktopGrid(m_currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_active = true;
|
||||||
|
effects->setActiveFullScreenEffect(this);
|
||||||
|
effects->addRepaintFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlideEffect::desktopChangingCancelled()
|
||||||
|
{
|
||||||
|
if (effects->hasActiveFullScreenEffect() && effects->activeFullScreenEffect() != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gestureActive = false;
|
||||||
|
startAnimation(effects->currentDesktop(), effects->currentDesktop(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF SlideEffect::moveInsideDesktopGrid(QPointF p)
|
||||||
|
{
|
||||||
|
if (p.x() < 0) {
|
||||||
|
p.setX(0);
|
||||||
|
}
|
||||||
|
if (p.y() < 0) {
|
||||||
|
p.setY(0);
|
||||||
|
}
|
||||||
|
if (p.x() > effects->desktopGridWidth() - 1) {
|
||||||
|
p.setX(effects->desktopGridWidth() - 1);
|
||||||
|
}
|
||||||
|
if (p.y() > effects->desktopGridHeight() - 1) {
|
||||||
|
p.setY(effects->desktopGridHeight() - 1);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideEffect::windowAdded(EffectWindow *w)
|
void SlideEffect::windowAdded(EffectWindow *w)
|
||||||
|
@ -431,4 +503,76 @@ void SlideEffect::windowDeleted(EffectWindow *w)
|
||||||
m_paintCtx.fullscreenWindows.removeAll(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())) > w / 2.0) {
|
||||||
|
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
|
} // namespace KWin
|
||||||
|
|
|
@ -18,6 +18,31 @@
|
||||||
namespace KWin
|
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
|
class SlideEffect : public Effect
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -60,41 +85,50 @@ public:
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void desktopChanged(int old, int current, EffectWindow *with);
|
void desktopChanged(int old, int current, EffectWindow *with);
|
||||||
|
void desktopChanging(uint old, QPointF desktopOffset, EffectWindow* with);
|
||||||
|
void desktopChangingCancelled();
|
||||||
void windowAdded(EffectWindow *w);
|
void windowAdded(EffectWindow *w);
|
||||||
void windowDeleted(EffectWindow *w);
|
void windowDeleted(EffectWindow *w);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPoint desktopCoords(int id) const;
|
QPoint getDrawCoords(QPointF pos, EffectScreen *screen);
|
||||||
QRect desktopGeometry(int id) const;
|
|
||||||
int workspaceWidth() const;
|
|
||||||
int workspaceHeight() const;
|
|
||||||
|
|
||||||
bool isTranslated(const EffectWindow *w) const;
|
bool isTranslated(const EffectWindow *w) const;
|
||||||
bool isPainted(const EffectWindow *w) const;
|
bool isPainted(const EffectWindow *w) const;
|
||||||
bool shouldElevate(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 startAnimation(int old, int current, EffectWindow *movingWindow = nullptr);
|
||||||
void stop();
|
void finishedSwitching();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_hGap;
|
int m_hGap;
|
||||||
int m_vGap;
|
int m_vGap;
|
||||||
bool m_slideDocks;
|
bool m_slideDocks;
|
||||||
bool m_slideBackground;
|
bool m_slideBackground;
|
||||||
|
int m_fullAnimationDuration; // Miliseconds for 1 complete desktop switch
|
||||||
|
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
TimeLine m_timeLine;
|
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;
|
EffectWindow *m_movingWindow = nullptr;
|
||||||
std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero();
|
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
|
struct
|
||||||
{
|
{
|
||||||
int desktop;
|
int desktop;
|
||||||
bool firstPass;
|
bool firstPass;
|
||||||
bool lastPass;
|
bool lastPass;
|
||||||
QPoint translation;
|
QPointF translation; //Uses desktops as units
|
||||||
|
|
||||||
QPoint currentPos;
|
QPoint currentPos;
|
||||||
QVector<int> visibleDesktops;
|
QVector<int> visibleDesktops;
|
||||||
|
@ -106,7 +140,7 @@ private:
|
||||||
|
|
||||||
inline int SlideEffect::duration() const
|
inline int SlideEffect::duration() const
|
||||||
{
|
{
|
||||||
return m_timeLine.duration().count();
|
return m_fullAnimationDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int SlideEffect::horizontalGap() const
|
inline int SlideEffect::horizontalGap() const
|
||||||
|
|
|
@ -800,7 +800,7 @@ void VirtualDesktopManager::initShortcuts()
|
||||||
// Gestures
|
// Gestures
|
||||||
// These connections decide which desktop to end on after gesture ends
|
// These connections decide which desktop to end on after gesture ends
|
||||||
connect(m_swipeGestureReleasedX.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedX);
|
connect(m_swipeGestureReleasedX.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedX);
|
||||||
connect(m_swipeGestureReleasedY, &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedY);
|
connect(m_swipeGestureReleasedY.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedY);
|
||||||
|
|
||||||
//These take the live feedback from a gesture
|
//These take the live feedback from a gesture
|
||||||
input()->registerRealtimeTouchpadSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), [this](qreal cb) {
|
input()->registerRealtimeTouchpadSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), [this](qreal cb) {
|
||||||
|
|
Loading…
Reference in a new issue