File indexing completed on 2024-11-10 04:57:09

0001 /*
0002     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0003     SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
0004     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "plugins/slideback/motionmanager.h"
0010 
0011 namespace KWin
0012 {
0013 
0014 /***************************************************************
0015  Motion1D
0016 ***************************************************************/
0017 
0018 Motion1D::Motion1D(double initial, double strength, double smoothness)
0019     : Motion<double>(initial, strength, smoothness)
0020 {
0021 }
0022 
0023 Motion1D::Motion1D(const Motion1D &other)
0024     : Motion<double>(other)
0025 {
0026 }
0027 
0028 Motion1D::~Motion1D()
0029 {
0030 }
0031 
0032 /***************************************************************
0033  Motion2D
0034 ***************************************************************/
0035 
0036 Motion2D::Motion2D(QPointF initial, double strength, double smoothness)
0037     : Motion<QPointF>(initial, strength, smoothness)
0038 {
0039 }
0040 
0041 Motion2D::Motion2D(const Motion2D &other)
0042     : Motion<QPointF>(other)
0043 {
0044 }
0045 
0046 Motion2D::~Motion2D()
0047 {
0048 }
0049 
0050 /***************************************************************
0051  WindowMotionManager
0052 ***************************************************************/
0053 
0054 WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier)
0055     : m_useGlobalAnimationModifier(useGlobalAnimationModifier)
0056 
0057 {
0058     // TODO: Allow developer to modify motion attributes
0059 } // TODO: What happens when the window moves by an external force?
0060 
0061 WindowMotionManager::~WindowMotionManager()
0062 {
0063 }
0064 
0065 void WindowMotionManager::manage(EffectWindow *w)
0066 {
0067     if (m_managedWindows.contains(w)) {
0068         return;
0069     }
0070 
0071     double strength = 0.08;
0072     double smoothness = 4.0;
0073     if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) {
0074         // If the factor is == 0 then we just skip the calculation completely
0075         strength = 0.08 / effects->animationTimeFactor();
0076         smoothness = effects->animationTimeFactor() * 4.0;
0077     }
0078 
0079     WindowMotion &motion = m_managedWindows[w];
0080     motion.translation.setStrength(strength);
0081     motion.translation.setSmoothness(smoothness);
0082     motion.scale.setStrength(strength * 1.33);
0083     motion.scale.setSmoothness(smoothness / 2.0);
0084 
0085     motion.translation.setValue(w->pos());
0086     motion.scale.setValue(QPointF(1.0, 1.0));
0087 }
0088 
0089 void WindowMotionManager::unmanage(EffectWindow *w)
0090 {
0091     m_movingWindowsSet.remove(w);
0092     m_managedWindows.remove(w);
0093 }
0094 
0095 void WindowMotionManager::unmanageAll()
0096 {
0097     m_managedWindows.clear();
0098     m_movingWindowsSet.clear();
0099 }
0100 
0101 void WindowMotionManager::calculate(int time)
0102 {
0103     if (!effects->animationTimeFactor()) {
0104         // Just skip it completely if the user wants no animation
0105         m_movingWindowsSet.clear();
0106         QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin();
0107         for (; it != m_managedWindows.end(); ++it) {
0108             WindowMotion *motion = &it.value();
0109             motion->translation.finish();
0110             motion->scale.finish();
0111         }
0112     }
0113 
0114     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin();
0115     for (; it != m_managedWindows.end(); ++it) {
0116         WindowMotion *motion = &it.value();
0117         int stopped = 0;
0118 
0119         // TODO: What happens when distance() == 0 but we are still moving fast?
0120         // TODO: Motion needs to be calculated from the window's center
0121 
0122         Motion2D *trans = &motion->translation;
0123         if (trans->distance().isNull()) {
0124             ++stopped;
0125         } else {
0126             // Still moving
0127             trans->calculate(time);
0128             const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1;
0129             const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1;
0130             if (trans->distance().x() * fx / 0.5 < 1.0 && trans->velocity().x() * fx / 0.2 < 1.0
0131                 && trans->distance().y() * fy / 0.5 < 1.0 && trans->velocity().y() * fy / 0.2 < 1.0) {
0132                 // Hide tiny oscillations
0133                 motion->translation.finish();
0134                 ++stopped;
0135             }
0136         }
0137 
0138         Motion2D *scale = &motion->scale;
0139         if (scale->distance().isNull()) {
0140             ++stopped;
0141         } else {
0142             // Still scaling
0143             scale->calculate(time);
0144             const short fx = scale->target().x() < 1.0 ? -1 : 1;
0145             const short fy = scale->target().y() < 1.0 ? -1 : 1;
0146             if (scale->distance().x() * fx / 0.001 < 1.0 && scale->velocity().x() * fx / 0.05 < 1.0
0147                 && scale->distance().y() * fy / 0.001 < 1.0 && scale->velocity().y() * fy / 0.05 < 1.0) {
0148                 // Hide tiny oscillations
0149                 motion->scale.finish();
0150                 ++stopped;
0151             }
0152         }
0153 
0154         // We just finished this window's motion
0155         if (stopped == 2) {
0156             m_movingWindowsSet.remove(it.key());
0157         }
0158     }
0159 }
0160 
0161 void WindowMotionManager::reset()
0162 {
0163     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin();
0164     for (; it != m_managedWindows.end(); ++it) {
0165         WindowMotion *motion = &it.value();
0166         EffectWindow *window = it.key();
0167         motion->translation.setTarget(window->pos());
0168         motion->translation.finish();
0169         motion->scale.setTarget(QPointF(1.0, 1.0));
0170         motion->scale.finish();
0171     }
0172 }
0173 
0174 void WindowMotionManager::reset(EffectWindow *w)
0175 {
0176     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w);
0177     if (it == m_managedWindows.end()) {
0178         return;
0179     }
0180 
0181     WindowMotion *motion = &it.value();
0182     motion->translation.setTarget(w->pos());
0183     motion->translation.finish();
0184     motion->scale.setTarget(QPointF(1.0, 1.0));
0185     motion->scale.finish();
0186 }
0187 
0188 void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data)
0189 {
0190     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w);
0191     if (it == m_managedWindows.end()) {
0192         return;
0193     }
0194 
0195     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
0196     WindowMotion *motion = &it.value();
0197     data += (motion->translation.value() - QPointF(w->x(), w->y()));
0198     data *= QVector2D(motion->scale.value());
0199 }
0200 
0201 void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale)
0202 {
0203     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w);
0204     Q_ASSERT(it != m_managedWindows.end()); // Notify the effect author that they did something wrong
0205 
0206     WindowMotion *motion = &it.value();
0207 
0208     if (yScale == 0.0) {
0209         yScale = scale;
0210     }
0211     QPointF scalePoint(scale, yScale);
0212 
0213     if (motion->translation.value() == target && motion->scale.value() == scalePoint) {
0214         return; // Window already at that position
0215     }
0216 
0217     motion->translation.setTarget(target);
0218     motion->scale.setTarget(scalePoint);
0219 
0220     m_movingWindowsSet << w;
0221 }
0222 
0223 QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const
0224 {
0225     QHash<EffectWindow *, WindowMotion>::const_iterator it = m_managedWindows.constFind(w);
0226     if (it == m_managedWindows.end()) {
0227         return w->frameGeometry();
0228     }
0229 
0230     const WindowMotion *motion = &it.value();
0231     QRectF geometry(w->frameGeometry());
0232 
0233     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
0234     geometry.moveTo(motion->translation.value());
0235     geometry.setWidth(geometry.width() * motion->scale.value().x());
0236     geometry.setHeight(geometry.height() * motion->scale.value().y());
0237 
0238     return geometry;
0239 }
0240 
0241 void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry)
0242 {
0243     QHash<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.find(w);
0244     if (it == m_managedWindows.end()) {
0245         return;
0246     }
0247     WindowMotion *motion = &it.value();
0248     motion->translation.setValue(geometry.topLeft());
0249     motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height())));
0250 }
0251 
0252 QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const
0253 {
0254     QHash<EffectWindow *, WindowMotion>::const_iterator it = m_managedWindows.constFind(w);
0255     if (it == m_managedWindows.end()) {
0256         return w->frameGeometry();
0257     }
0258 
0259     const WindowMotion *motion = &it.value();
0260     QRectF geometry(w->frameGeometry());
0261 
0262     // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid)
0263     geometry.moveTo(motion->translation.target());
0264     geometry.setWidth(geometry.width() * motion->scale.target().x());
0265     geometry.setHeight(geometry.height() * motion->scale.target().y());
0266 
0267     return geometry;
0268 }
0269 
0270 EffectWindow *WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const
0271 {
0272     // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows
0273     QHash<EffectWindow *, WindowMotion>::ConstIterator it = m_managedWindows.constBegin();
0274     while (it != m_managedWindows.constEnd()) {
0275         if (exclusiveContains(transformedGeometry(it.key()), point)) {
0276             return it.key();
0277         }
0278         ++it;
0279     }
0280 
0281     return nullptr;
0282 }
0283 
0284 } // namespace KWin