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