diff --git a/src/libkwineffects/effects.cpp b/src/libkwineffects/effects.cpp index 431ce76fca..84d89cfbdc 100644 --- a/src/libkwineffects/effects.cpp +++ b/src/libkwineffects/effects.cpp @@ -2412,276 +2412,6 @@ void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatr } } -/*************************************************************** - Motion1D -***************************************************************/ - -Motion1D::Motion1D(double initial, double strength, double smoothness) - : Motion(initial, strength, smoothness) -{ -} - -Motion1D::Motion1D(const Motion1D &other) - : Motion(other) -{ -} - -Motion1D::~Motion1D() -{ -} - -/*************************************************************** - Motion2D -***************************************************************/ - -Motion2D::Motion2D(QPointF initial, double strength, double smoothness) - : Motion(initial, strength, smoothness) -{ -} - -Motion2D::Motion2D(const Motion2D &other) - : Motion(other) -{ -} - -Motion2D::~Motion2D() -{ -} - -/*************************************************************** - WindowMotionManager -***************************************************************/ - -WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier) - : m_useGlobalAnimationModifier(useGlobalAnimationModifier) - -{ - // TODO: Allow developer to modify motion attributes -} // TODO: What happens when the window moves by an external force? - -WindowMotionManager::~WindowMotionManager() -{ -} - -void WindowMotionManager::manage(EffectWindow *w) -{ - if (m_managedWindows.contains(w)) { - return; - } - - double strength = 0.08; - double smoothness = 4.0; - if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) { - // If the factor is == 0 then we just skip the calculation completely - strength = 0.08 / effects->animationTimeFactor(); - smoothness = effects->animationTimeFactor() * 4.0; - } - - WindowMotion &motion = m_managedWindows[w]; - motion.translation.setStrength(strength); - motion.translation.setSmoothness(smoothness); - motion.scale.setStrength(strength * 1.33); - motion.scale.setSmoothness(smoothness / 2.0); - - motion.translation.setValue(w->pos()); - motion.scale.setValue(QPointF(1.0, 1.0)); -} - -void WindowMotionManager::unmanage(EffectWindow *w) -{ - m_movingWindowsSet.remove(w); - m_managedWindows.remove(w); -} - -void WindowMotionManager::unmanageAll() -{ - m_managedWindows.clear(); - m_movingWindowsSet.clear(); -} - -void WindowMotionManager::calculate(int time) -{ - if (!effects->animationTimeFactor()) { - // Just skip it completely if the user wants no animation - m_movingWindowsSet.clear(); - QHash::iterator it = m_managedWindows.begin(); - for (; it != m_managedWindows.end(); ++it) { - WindowMotion *motion = &it.value(); - motion->translation.finish(); - motion->scale.finish(); - } - } - - QHash::iterator it = m_managedWindows.begin(); - for (; it != m_managedWindows.end(); ++it) { - WindowMotion *motion = &it.value(); - int stopped = 0; - - // TODO: What happens when distance() == 0 but we are still moving fast? - // TODO: Motion needs to be calculated from the window's center - - Motion2D *trans = &motion->translation; - if (trans->distance().isNull()) { - ++stopped; - } else { - // Still moving - trans->calculate(time); - const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1; - const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1; - if (trans->distance().x() * fx / 0.5 < 1.0 && trans->velocity().x() * fx / 0.2 < 1.0 - && trans->distance().y() * fy / 0.5 < 1.0 && trans->velocity().y() * fy / 0.2 < 1.0) { - // Hide tiny oscillations - motion->translation.finish(); - ++stopped; - } - } - - Motion2D *scale = &motion->scale; - if (scale->distance().isNull()) { - ++stopped; - } else { - // Still scaling - scale->calculate(time); - const short fx = scale->target().x() < 1.0 ? -1 : 1; - const short fy = scale->target().y() < 1.0 ? -1 : 1; - if (scale->distance().x() * fx / 0.001 < 1.0 && scale->velocity().x() * fx / 0.05 < 1.0 - && scale->distance().y() * fy / 0.001 < 1.0 && scale->velocity().y() * fy / 0.05 < 1.0) { - // Hide tiny oscillations - motion->scale.finish(); - ++stopped; - } - } - - // We just finished this window's motion - if (stopped == 2) { - m_movingWindowsSet.remove(it.key()); - } - } -} - -void WindowMotionManager::reset() -{ - QHash::iterator it = m_managedWindows.begin(); - for (; it != m_managedWindows.end(); ++it) { - WindowMotion *motion = &it.value(); - EffectWindow *window = it.key(); - motion->translation.setTarget(window->pos()); - motion->translation.finish(); - motion->scale.setTarget(QPointF(1.0, 1.0)); - motion->scale.finish(); - } -} - -void WindowMotionManager::reset(EffectWindow *w) -{ - QHash::iterator it = m_managedWindows.find(w); - if (it == m_managedWindows.end()) { - return; - } - - WindowMotion *motion = &it.value(); - motion->translation.setTarget(w->pos()); - motion->translation.finish(); - motion->scale.setTarget(QPointF(1.0, 1.0)); - motion->scale.finish(); -} - -void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data) -{ - QHash::iterator it = m_managedWindows.find(w); - if (it == m_managedWindows.end()) { - return; - } - - // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) - WindowMotion *motion = &it.value(); - data += (motion->translation.value() - QPointF(w->x(), w->y())); - data *= QVector2D(motion->scale.value()); -} - -void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale) -{ - QHash::iterator it = m_managedWindows.find(w); - Q_ASSERT(it != m_managedWindows.end()); // Notify the effect author that they did something wrong - - WindowMotion *motion = &it.value(); - - if (yScale == 0.0) { - yScale = scale; - } - QPointF scalePoint(scale, yScale); - - if (motion->translation.value() == target && motion->scale.value() == scalePoint) { - return; // Window already at that position - } - - motion->translation.setTarget(target); - motion->scale.setTarget(scalePoint); - - m_movingWindowsSet << w; -} - -QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const -{ - QHash::const_iterator it = m_managedWindows.constFind(w); - if (it == m_managedWindows.end()) { - return w->frameGeometry(); - } - - const WindowMotion *motion = &it.value(); - QRectF geometry(w->frameGeometry()); - - // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) - geometry.moveTo(motion->translation.value()); - geometry.setWidth(geometry.width() * motion->scale.value().x()); - geometry.setHeight(geometry.height() * motion->scale.value().y()); - - return geometry; -} - -void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry) -{ - QHash::iterator it = m_managedWindows.find(w); - if (it == m_managedWindows.end()) { - return; - } - WindowMotion *motion = &it.value(); - motion->translation.setValue(geometry.topLeft()); - motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height()))); -} - -QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const -{ - QHash::const_iterator it = m_managedWindows.constFind(w); - if (it == m_managedWindows.end()) { - return w->frameGeometry(); - } - - const WindowMotion *motion = &it.value(); - QRectF geometry(w->frameGeometry()); - - // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) - geometry.moveTo(motion->translation.target()); - geometry.setWidth(geometry.width() * motion->scale.target().x()); - geometry.setHeight(geometry.height() * motion->scale.target().y()); - - return geometry; -} - -EffectWindow *WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const -{ - // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows - QHash::ConstIterator it = m_managedWindows.constBegin(); - while (it != m_managedWindows.constEnd()) { - if (exclusiveContains(transformedGeometry(it.key()), point)) { - return it.key(); - } - ++it; - } - - return nullptr; -} - } // namespace #include "moc_effects.cpp" diff --git a/src/libkwineffects/effects.h b/src/libkwineffects/effects.h index 7ac7bb287e..65931055d4 100644 --- a/src/libkwineffects/effects.h +++ b/src/libkwineffects/effects.h @@ -2294,300 +2294,6 @@ private: VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round; }; -/** - * @internal - */ -template -class KWIN_EXPORT Motion -{ -public: - /** - * Creates a new motion object. "Strength" is the amount of - * acceleration that is applied to the object when the target - * changes and "smoothness" relates to how fast the object - * can change its direction and speed. - */ - explicit Motion(T initial, double strength, double smoothness); - /** - * Creates an exact copy of another motion object, including - * position, target and velocity. - */ - Motion(const Motion &other); - ~Motion(); - - inline T value() const - { - return m_value; - } - inline void setValue(const T value) - { - m_value = value; - } - inline T target() const - { - return m_target; - } - inline void setTarget(const T target) - { - m_start = m_value; - m_target = target; - } - inline T velocity() const - { - return m_velocity; - } - inline void setVelocity(const T velocity) - { - m_velocity = velocity; - } - - inline double strength() const - { - return m_strength; - } - inline void setStrength(const double strength) - { - m_strength = strength; - } - inline double smoothness() const - { - return m_smoothness; - } - inline void setSmoothness(const double smoothness) - { - m_smoothness = smoothness; - } - inline T startValue() - { - return m_start; - } - - /** - * The distance between the current position and the target. - */ - inline T distance() const - { - return m_target - m_value; - } - - /** - * Calculates the new position if not at the target. Called - * once per frame only. - */ - void calculate(const int msec); - /** - * Place the object on top of the target immediately, - * bypassing all movement calculation. - */ - void finish(); - -private: - T m_value; - T m_start; - T m_target; - T m_velocity; - double m_strength; - double m_smoothness; -}; - -/** - * @short A single 1D motion dynamics object. - * - * This class represents a single object that can be moved around a - * 1D space. Although it can be used directly by itself it is - * recommended to use a motion manager instead. - */ -class KWIN_EXPORT Motion1D : public Motion -{ -public: - explicit Motion1D(double initial = 0.0, double strength = 0.08, double smoothness = 4.0); - Motion1D(const Motion1D &other); - ~Motion1D(); -}; - -/** - * @short A single 2D motion dynamics object. - * - * This class represents a single object that can be moved around a - * 2D space. Although it can be used directly by itself it is - * recommended to use a motion manager instead. - */ -class KWIN_EXPORT Motion2D : public Motion -{ -public: - explicit Motion2D(QPointF initial = QPointF(), double strength = 0.08, double smoothness = 4.0); - Motion2D(const Motion2D &other); - ~Motion2D(); -}; - -/** - * @short Helper class for motion dynamics in KWin effects. - * - * This motion manager class is intended to help KWin effect authors - * move windows across the screen smoothly and naturally. Once - * windows are registered by the manager the effect can issue move - * commands with the moveWindow() methods. The position of any - * managed window can be determined in realtime by the - * transformedGeometry() method. As the manager knows if any windows - * are moving at any given time it can also be used as a notifier as - * to see whether the effect is active or not. - */ -class KWIN_EXPORT WindowMotionManager -{ -public: - /** - * Creates a new window manager object. - */ - explicit WindowMotionManager(bool useGlobalAnimationModifier = true); - ~WindowMotionManager(); - - /** - * Register a window for managing. - */ - void manage(EffectWindow *w); - /** - * Register a list of windows for managing. - */ - inline void manage(const EffectWindowList &list) - { - for (int i = 0; i < list.size(); i++) { - manage(list.at(i)); - } - } - /** - * Deregister a window. All transformations applied to the - * window will be permanently removed and cannot be recovered. - */ - void unmanage(EffectWindow *w); - /** - * Deregister all windows, returning the manager to its - * originally initiated state. - */ - void unmanageAll(); - /** - * Determine the new positions for windows that have not - * reached their target. Called once per frame, usually in - * prePaintScreen(). Remember to set the - * Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS flag. - */ - void calculate(int time); - /** - * Modify a registered window's paint data to make it appear - * at its real location on the screen. Usually called in - * paintWindow(). Remember to flag the window as having been - * transformed in prePaintWindow() by calling - * WindowPrePaintData::setTransformed() - */ - void apply(EffectWindow *w, WindowPaintData &data); - /** - * Set all motion targets and values back to where the - * windows were before transformations. The same as - * unmanaging then remanaging all windows. - */ - void reset(); - /** - * Resets the motion target and current value of a single - * window. - */ - void reset(EffectWindow *w); - - /** - * Ask the manager to move the window to the target position - * with the specified scale. If `yScale` is not provided or - * set to 0.0, `scale` will be used as the scale in the - * vertical direction as well as in the horizontal direction. - */ - void moveWindow(EffectWindow *w, QPoint target, double scale = 1.0, double yScale = 0.0); - /** - * This is an overloaded method, provided for convenience. - * - * Ask the manager to move the window to the target rectangle. - * Automatically determines scale. - */ - inline void moveWindow(EffectWindow *w, QRect target) - { - // TODO: Scale might be slightly different in the comparison due to rounding - moveWindow(w, target.topLeft(), - target.width() / double(w->width()), target.height() / double(w->height())); - } - - /** - * Retrieve the current tranformed geometry of a registered - * window. - */ - QRectF transformedGeometry(EffectWindow *w) const; - /** - * Sets the current transformed geometry of a registered window to the given geometry. - * @see transformedGeometry - * @since 4.5 - */ - void setTransformedGeometry(EffectWindow *w, const QRectF &geometry); - /** - * Retrieve the current target geometry of a registered - * window. - */ - QRectF targetGeometry(EffectWindow *w) const; - /** - * Return the window that has its transformed geometry under - * the specified point. It is recommended to use the stacking - * order as it's what the user sees, but it is slightly - * slower to process. - */ - EffectWindow *windowAtPoint(QPoint point, bool useStackingOrder = true) const; - - /** - * Return a list of all currently registered windows. - */ - inline EffectWindowList managedWindows() const - { - return m_managedWindows.keys(); - } - /** - * Returns whether or not a specified window is being managed - * by this manager object. - */ - inline bool isManaging(EffectWindow *w) const - { - return m_managedWindows.contains(w); - } - /** - * Returns whether or not this manager object is actually - * managing any windows or not. - */ - inline bool managingWindows() const - { - return !m_managedWindows.empty(); - } - /** - * Returns whether all windows have reached their targets yet - * or not. Can be used to see if an effect should be - * processed and displayed or not. - */ - inline bool areWindowsMoving() const - { - return !m_movingWindowsSet.isEmpty(); - } - /** - * Returns whether a window has reached its targets yet - * or not. - */ - inline bool isWindowMoving(EffectWindow *w) const - { - return m_movingWindowsSet.contains(w); - } - -private: - bool m_useGlobalAnimationModifier; - struct WindowMotion - { - // TODO: Rotation, etc? - Motion2D translation; // Absolute position - Motion2D scale; // xScale and yScale - }; - QHash m_managedWindows; - QSet m_movingWindowsSet; -}; - /** * Pointer to the global EffectsHandler object. */ @@ -2682,61 +2388,6 @@ inline QRectF WindowQuad::bounds() const return QRectF(QPointF(left(), top()), QPointF(right(), bottom())); } -/*************************************************************** - Motion -***************************************************************/ - -template -Motion::Motion(T initial, double strength, double smoothness) - : m_value(initial) - , m_start(initial) - , m_target(initial) - , m_velocity() - , m_strength(strength) - , m_smoothness(smoothness) -{ -} - -template -Motion::Motion(const Motion &other) - : m_value(other.value()) - , m_start(other.target()) - , m_target(other.target()) - , m_velocity(other.velocity()) - , m_strength(other.strength()) - , m_smoothness(other.smoothness()) -{ -} - -template -Motion::~Motion() -{ -} - -template -void Motion::calculate(const int msec) -{ - if (m_value == m_target && m_velocity == T()) { // At target and not moving - return; - } - - // Poor man's time independent calculation - int steps = std::max(1, msec / 5); - for (int i = 0; i < steps; i++) { - T diff = m_target - m_value; - T strength = diff * m_strength; - m_velocity = (m_smoothness * m_velocity + strength) / (m_smoothness + 1.0); - m_value += m_velocity; - } -} - -template -void Motion::finish() -{ - m_value = m_target; - m_velocity = T(); -} - /*************************************************************** EffectWindow ***************************************************************/ diff --git a/src/plugins/slideback/CMakeLists.txt b/src/plugins/slideback/CMakeLists.txt index fdadc4ffaf..152e28fa96 100644 --- a/src/plugins/slideback/CMakeLists.txt +++ b/src/plugins/slideback/CMakeLists.txt @@ -1,13 +1,12 @@ ####################################### # Effect -# Source files -set(slideback_SOURCES +kwin_add_builtin_effect(slideback) +target_sources(slideback PRIVATE main.cpp + motionmanager.cpp slideback.cpp ) - -kwin_add_builtin_effect(slideback ${slideback_SOURCES}) target_link_libraries(slideback PRIVATE kwin ) diff --git a/src/plugins/slideback/motionmanager.cpp b/src/plugins/slideback/motionmanager.cpp new file mode 100644 index 0000000000..b510a40581 --- /dev/null +++ b/src/plugins/slideback/motionmanager.cpp @@ -0,0 +1,284 @@ +/* + SPDX-FileCopyrightText: 2006 Lubos Lunak + SPDX-FileCopyrightText: 2009 Lucas Murray + SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "plugins/slideback/motionmanager.h" + +namespace KWin +{ + +/*************************************************************** + Motion1D +***************************************************************/ + +Motion1D::Motion1D(double initial, double strength, double smoothness) + : Motion(initial, strength, smoothness) +{ +} + +Motion1D::Motion1D(const Motion1D &other) + : Motion(other) +{ +} + +Motion1D::~Motion1D() +{ +} + +/*************************************************************** + Motion2D +***************************************************************/ + +Motion2D::Motion2D(QPointF initial, double strength, double smoothness) + : Motion(initial, strength, smoothness) +{ +} + +Motion2D::Motion2D(const Motion2D &other) + : Motion(other) +{ +} + +Motion2D::~Motion2D() +{ +} + +/*************************************************************** + WindowMotionManager +***************************************************************/ + +WindowMotionManager::WindowMotionManager(bool useGlobalAnimationModifier) + : m_useGlobalAnimationModifier(useGlobalAnimationModifier) + +{ + // TODO: Allow developer to modify motion attributes +} // TODO: What happens when the window moves by an external force? + +WindowMotionManager::~WindowMotionManager() +{ +} + +void WindowMotionManager::manage(EffectWindow *w) +{ + if (m_managedWindows.contains(w)) { + return; + } + + double strength = 0.08; + double smoothness = 4.0; + if (m_useGlobalAnimationModifier && effects->animationTimeFactor()) { + // If the factor is == 0 then we just skip the calculation completely + strength = 0.08 / effects->animationTimeFactor(); + smoothness = effects->animationTimeFactor() * 4.0; + } + + WindowMotion &motion = m_managedWindows[w]; + motion.translation.setStrength(strength); + motion.translation.setSmoothness(smoothness); + motion.scale.setStrength(strength * 1.33); + motion.scale.setSmoothness(smoothness / 2.0); + + motion.translation.setValue(w->pos()); + motion.scale.setValue(QPointF(1.0, 1.0)); +} + +void WindowMotionManager::unmanage(EffectWindow *w) +{ + m_movingWindowsSet.remove(w); + m_managedWindows.remove(w); +} + +void WindowMotionManager::unmanageAll() +{ + m_managedWindows.clear(); + m_movingWindowsSet.clear(); +} + +void WindowMotionManager::calculate(int time) +{ + if (!effects->animationTimeFactor()) { + // Just skip it completely if the user wants no animation + m_movingWindowsSet.clear(); + QHash::iterator it = m_managedWindows.begin(); + for (; it != m_managedWindows.end(); ++it) { + WindowMotion *motion = &it.value(); + motion->translation.finish(); + motion->scale.finish(); + } + } + + QHash::iterator it = m_managedWindows.begin(); + for (; it != m_managedWindows.end(); ++it) { + WindowMotion *motion = &it.value(); + int stopped = 0; + + // TODO: What happens when distance() == 0 but we are still moving fast? + // TODO: Motion needs to be calculated from the window's center + + Motion2D *trans = &motion->translation; + if (trans->distance().isNull()) { + ++stopped; + } else { + // Still moving + trans->calculate(time); + const short fx = trans->target().x() <= trans->startValue().x() ? -1 : 1; + const short fy = trans->target().y() <= trans->startValue().y() ? -1 : 1; + if (trans->distance().x() * fx / 0.5 < 1.0 && trans->velocity().x() * fx / 0.2 < 1.0 + && trans->distance().y() * fy / 0.5 < 1.0 && trans->velocity().y() * fy / 0.2 < 1.0) { + // Hide tiny oscillations + motion->translation.finish(); + ++stopped; + } + } + + Motion2D *scale = &motion->scale; + if (scale->distance().isNull()) { + ++stopped; + } else { + // Still scaling + scale->calculate(time); + const short fx = scale->target().x() < 1.0 ? -1 : 1; + const short fy = scale->target().y() < 1.0 ? -1 : 1; + if (scale->distance().x() * fx / 0.001 < 1.0 && scale->velocity().x() * fx / 0.05 < 1.0 + && scale->distance().y() * fy / 0.001 < 1.0 && scale->velocity().y() * fy / 0.05 < 1.0) { + // Hide tiny oscillations + motion->scale.finish(); + ++stopped; + } + } + + // We just finished this window's motion + if (stopped == 2) { + m_movingWindowsSet.remove(it.key()); + } + } +} + +void WindowMotionManager::reset() +{ + QHash::iterator it = m_managedWindows.begin(); + for (; it != m_managedWindows.end(); ++it) { + WindowMotion *motion = &it.value(); + EffectWindow *window = it.key(); + motion->translation.setTarget(window->pos()); + motion->translation.finish(); + motion->scale.setTarget(QPointF(1.0, 1.0)); + motion->scale.finish(); + } +} + +void WindowMotionManager::reset(EffectWindow *w) +{ + QHash::iterator it = m_managedWindows.find(w); + if (it == m_managedWindows.end()) { + return; + } + + WindowMotion *motion = &it.value(); + motion->translation.setTarget(w->pos()); + motion->translation.finish(); + motion->scale.setTarget(QPointF(1.0, 1.0)); + motion->scale.finish(); +} + +void WindowMotionManager::apply(EffectWindow *w, WindowPaintData &data) +{ + QHash::iterator it = m_managedWindows.find(w); + if (it == m_managedWindows.end()) { + return; + } + + // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) + WindowMotion *motion = &it.value(); + data += (motion->translation.value() - QPointF(w->x(), w->y())); + data *= QVector2D(motion->scale.value()); +} + +void WindowMotionManager::moveWindow(EffectWindow *w, QPoint target, double scale, double yScale) +{ + QHash::iterator it = m_managedWindows.find(w); + Q_ASSERT(it != m_managedWindows.end()); // Notify the effect author that they did something wrong + + WindowMotion *motion = &it.value(); + + if (yScale == 0.0) { + yScale = scale; + } + QPointF scalePoint(scale, yScale); + + if (motion->translation.value() == target && motion->scale.value() == scalePoint) { + return; // Window already at that position + } + + motion->translation.setTarget(target); + motion->scale.setTarget(scalePoint); + + m_movingWindowsSet << w; +} + +QRectF WindowMotionManager::transformedGeometry(EffectWindow *w) const +{ + QHash::const_iterator it = m_managedWindows.constFind(w); + if (it == m_managedWindows.end()) { + return w->frameGeometry(); + } + + const WindowMotion *motion = &it.value(); + QRectF geometry(w->frameGeometry()); + + // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) + geometry.moveTo(motion->translation.value()); + geometry.setWidth(geometry.width() * motion->scale.value().x()); + geometry.setHeight(geometry.height() * motion->scale.value().y()); + + return geometry; +} + +void WindowMotionManager::setTransformedGeometry(EffectWindow *w, const QRectF &geometry) +{ + QHash::iterator it = m_managedWindows.find(w); + if (it == m_managedWindows.end()) { + return; + } + WindowMotion *motion = &it.value(); + motion->translation.setValue(geometry.topLeft()); + motion->scale.setValue(QPointF(geometry.width() / qreal(w->width()), geometry.height() / qreal(w->height()))); +} + +QRectF WindowMotionManager::targetGeometry(EffectWindow *w) const +{ + QHash::const_iterator it = m_managedWindows.constFind(w); + if (it == m_managedWindows.end()) { + return w->frameGeometry(); + } + + const WindowMotion *motion = &it.value(); + QRectF geometry(w->frameGeometry()); + + // TODO: Take into account existing scale so that we can work with multiple managers (E.g. Present windows + grid) + geometry.moveTo(motion->translation.target()); + geometry.setWidth(geometry.width() * motion->scale.target().x()); + geometry.setHeight(geometry.height() * motion->scale.target().y()); + + return geometry; +} + +EffectWindow *WindowMotionManager::windowAtPoint(QPoint point, bool useStackingOrder) const +{ + // TODO: Stacking order uses EffectsHandler::stackingOrder() then filters by m_managedWindows + QHash::ConstIterator it = m_managedWindows.constBegin(); + while (it != m_managedWindows.constEnd()) { + if (exclusiveContains(transformedGeometry(it.key()), point)) { + return it.key(); + } + ++it; + } + + return nullptr; +} + +} // namespace KWin diff --git a/src/plugins/slideback/motionmanager.h b/src/plugins/slideback/motionmanager.h new file mode 100644 index 0000000000..ba35f14866 --- /dev/null +++ b/src/plugins/slideback/motionmanager.h @@ -0,0 +1,361 @@ +/* + SPDX-FileCopyrightText: 2006 Lubos Lunak + SPDX-FileCopyrightText: 2009 Lucas Murray + SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "libkwineffects/effects.h" + +namespace KWin +{ + +/** + * @internal + */ +template +class KWIN_EXPORT Motion +{ +public: + /** + * Creates a new motion object. "Strength" is the amount of + * acceleration that is applied to the object when the target + * changes and "smoothness" relates to how fast the object + * can change its direction and speed. + */ + explicit Motion(T initial, double strength, double smoothness); + /** + * Creates an exact copy of another motion object, including + * position, target and velocity. + */ + Motion(const Motion &other); + ~Motion(); + + inline T value() const + { + return m_value; + } + inline void setValue(const T value) + { + m_value = value; + } + inline T target() const + { + return m_target; + } + inline void setTarget(const T target) + { + m_start = m_value; + m_target = target; + } + inline T velocity() const + { + return m_velocity; + } + inline void setVelocity(const T velocity) + { + m_velocity = velocity; + } + + inline double strength() const + { + return m_strength; + } + inline void setStrength(const double strength) + { + m_strength = strength; + } + inline double smoothness() const + { + return m_smoothness; + } + inline void setSmoothness(const double smoothness) + { + m_smoothness = smoothness; + } + inline T startValue() + { + return m_start; + } + + /** + * The distance between the current position and the target. + */ + inline T distance() const + { + return m_target - m_value; + } + + /** + * Calculates the new position if not at the target. Called + * once per frame only. + */ + void calculate(const int msec); + /** + * Place the object on top of the target immediately, + * bypassing all movement calculation. + */ + void finish(); + +private: + T m_value; + T m_start; + T m_target; + T m_velocity; + double m_strength; + double m_smoothness; +}; + +/** + * @short A single 1D motion dynamics object. + * + * This class represents a single object that can be moved around a + * 1D space. Although it can be used directly by itself it is + * recommended to use a motion manager instead. + */ +class KWIN_EXPORT Motion1D : public Motion +{ +public: + explicit Motion1D(double initial = 0.0, double strength = 0.08, double smoothness = 4.0); + Motion1D(const Motion1D &other); + ~Motion1D(); +}; + +/** + * @short A single 2D motion dynamics object. + * + * This class represents a single object that can be moved around a + * 2D space. Although it can be used directly by itself it is + * recommended to use a motion manager instead. + */ +class KWIN_EXPORT Motion2D : public Motion +{ +public: + explicit Motion2D(QPointF initial = QPointF(), double strength = 0.08, double smoothness = 4.0); + Motion2D(const Motion2D &other); + ~Motion2D(); +}; + +/** + * @short Helper class for motion dynamics in KWin effects. + * + * This motion manager class is intended to help KWin effect authors + * move windows across the screen smoothly and naturally. Once + * windows are registered by the manager the effect can issue move + * commands with the moveWindow() methods. The position of any + * managed window can be determined in realtime by the + * transformedGeometry() method. As the manager knows if any windows + * are moving at any given time it can also be used as a notifier as + * to see whether the effect is active or not. + */ +class KWIN_EXPORT WindowMotionManager +{ +public: + /** + * Creates a new window manager object. + */ + explicit WindowMotionManager(bool useGlobalAnimationModifier = true); + ~WindowMotionManager(); + + /** + * Register a window for managing. + */ + void manage(EffectWindow *w); + /** + * Register a list of windows for managing. + */ + inline void manage(const EffectWindowList &list) + { + for (int i = 0; i < list.size(); i++) { + manage(list.at(i)); + } + } + /** + * Deregister a window. All transformations applied to the + * window will be permanently removed and cannot be recovered. + */ + void unmanage(EffectWindow *w); + /** + * Deregister all windows, returning the manager to its + * originally initiated state. + */ + void unmanageAll(); + /** + * Determine the new positions for windows that have not + * reached their target. Called once per frame, usually in + * prePaintScreen(). Remember to set the + * Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS flag. + */ + void calculate(int time); + /** + * Modify a registered window's paint data to make it appear + * at its real location on the screen. Usually called in + * paintWindow(). Remember to flag the window as having been + * transformed in prePaintWindow() by calling + * WindowPrePaintData::setTransformed() + */ + void apply(EffectWindow *w, WindowPaintData &data); + /** + * Set all motion targets and values back to where the + * windows were before transformations. The same as + * unmanaging then remanaging all windows. + */ + void reset(); + /** + * Resets the motion target and current value of a single + * window. + */ + void reset(EffectWindow *w); + + /** + * Ask the manager to move the window to the target position + * with the specified scale. If `yScale` is not provided or + * set to 0.0, `scale` will be used as the scale in the + * vertical direction as well as in the horizontal direction. + */ + void moveWindow(EffectWindow *w, QPoint target, double scale = 1.0, double yScale = 0.0); + /** + * This is an overloaded method, provided for convenience. + * + * Ask the manager to move the window to the target rectangle. + * Automatically determines scale. + */ + inline void moveWindow(EffectWindow *w, QRect target) + { + // TODO: Scale might be slightly different in the comparison due to rounding + moveWindow(w, target.topLeft(), + target.width() / double(w->width()), target.height() / double(w->height())); + } + + /** + * Retrieve the current tranformed geometry of a registered + * window. + */ + QRectF transformedGeometry(EffectWindow *w) const; + /** + * Sets the current transformed geometry of a registered window to the given geometry. + * @see transformedGeometry + * @since 4.5 + */ + void setTransformedGeometry(EffectWindow *w, const QRectF &geometry); + /** + * Retrieve the current target geometry of a registered + * window. + */ + QRectF targetGeometry(EffectWindow *w) const; + /** + * Return the window that has its transformed geometry under + * the specified point. It is recommended to use the stacking + * order as it's what the user sees, but it is slightly + * slower to process. + */ + EffectWindow *windowAtPoint(QPoint point, bool useStackingOrder = true) const; + + /** + * Return a list of all currently registered windows. + */ + inline EffectWindowList managedWindows() const + { + return m_managedWindows.keys(); + } + /** + * Returns whether or not a specified window is being managed + * by this manager object. + */ + inline bool isManaging(EffectWindow *w) const + { + return m_managedWindows.contains(w); + } + /** + * Returns whether or not this manager object is actually + * managing any windows or not. + */ + inline bool managingWindows() const + { + return !m_managedWindows.empty(); + } + /** + * Returns whether all windows have reached their targets yet + * or not. Can be used to see if an effect should be + * processed and displayed or not. + */ + inline bool areWindowsMoving() const + { + return !m_movingWindowsSet.isEmpty(); + } + /** + * Returns whether a window has reached its targets yet + * or not. + */ + inline bool isWindowMoving(EffectWindow *w) const + { + return m_movingWindowsSet.contains(w); + } + +private: + bool m_useGlobalAnimationModifier; + struct WindowMotion + { + // TODO: Rotation, etc? + Motion2D translation; // Absolute position + Motion2D scale; // xScale and yScale + }; + QHash m_managedWindows; + QSet m_movingWindowsSet; +}; + +template +Motion::Motion(T initial, double strength, double smoothness) + : m_value(initial) + , m_start(initial) + , m_target(initial) + , m_velocity() + , m_strength(strength) + , m_smoothness(smoothness) +{ +} + +template +Motion::Motion(const Motion &other) + : m_value(other.value()) + , m_start(other.target()) + , m_target(other.target()) + , m_velocity(other.velocity()) + , m_strength(other.strength()) + , m_smoothness(other.smoothness()) +{ +} + +template +Motion::~Motion() +{ +} + +template +void Motion::calculate(const int msec) +{ + if (m_value == m_target && m_velocity == T()) { // At target and not moving + return; + } + + // Poor man's time independent calculation + int steps = std::max(1, msec / 5); + for (int i = 0; i < steps; i++) { + T diff = m_target - m_value; + T strength = diff * m_strength; + m_velocity = (m_smoothness * m_velocity + strength) / (m_smoothness + 1.0); + m_value += m_velocity; + } +} + +template +void Motion::finish() +{ + m_value = m_target; + m_velocity = T(); +} + +} // namespace KWin diff --git a/src/plugins/slideback/slideback.h b/src/plugins/slideback/slideback.h index 30b81bd99b..41241bd761 100644 --- a/src/plugins/slideback/slideback.h +++ b/src/plugins/slideback/slideback.h @@ -11,6 +11,7 @@ // Include with base class for effects. #include "libkwineffects/effects.h" +#include "plugins/slideback/motionmanager.h" namespace KWin {