Move window motion manager to the slideback effect

Slide back is the only remaining its user.
This commit is contained in:
Vlad Zahorodnii 2023-11-16 11:42:19 +02:00
parent b0292fc0de
commit 96e979c736
6 changed files with 649 additions and 623 deletions

View file

@ -2412,276 +2412,6 @@ void RenderGeometry::postProcessTextureCoordinates(const QMatrix4x4 &textureMatr
} }
} }
/***************************************************************
Motion1D
***************************************************************/
Motion1D::Motion1D(double initial, double strength, double smoothness)
: Motion<double>(initial, strength, smoothness)
{
}
Motion1D::Motion1D(const Motion1D &other)
: Motion<double>(other)
{
}
Motion1D::~Motion1D()
{
}
/***************************************************************
Motion2D
***************************************************************/
Motion2D::Motion2D(QPointF initial, double strength, double smoothness)
: Motion<QPointF>(initial, strength, smoothness)
{
}
Motion2D::Motion2D(const Motion2D &other)
: Motion<QPointF>(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<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin();
for (; it != m_managedWindows.end(); ++it) {
WindowMotion *motion = &it.value();
motion->translation.finish();
motion->scale.finish();
}
}
QHash<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::ConstIterator it = m_managedWindows.constBegin();
while (it != m_managedWindows.constEnd()) {
if (exclusiveContains(transformedGeometry(it.key()), point)) {
return it.key();
}
++it;
}
return nullptr;
}
} // namespace } // namespace
#include "moc_effects.cpp" #include "moc_effects.cpp"

View file

@ -2294,300 +2294,6 @@ private:
VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round; VertexSnappingMode m_vertexSnappingMode = VertexSnappingMode::Round;
}; };
/**
* @internal
*/
template<typename T>
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<T> &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<double>
{
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<QPointF>
{
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<EffectWindow *, WindowMotion> m_managedWindows;
QSet<EffectWindow *> m_movingWindowsSet;
};
/** /**
* Pointer to the global EffectsHandler object. * Pointer to the global EffectsHandler object.
*/ */
@ -2682,61 +2388,6 @@ inline QRectF WindowQuad::bounds() const
return QRectF(QPointF(left(), top()), QPointF(right(), bottom())); return QRectF(QPointF(left(), top()), QPointF(right(), bottom()));
} }
/***************************************************************
Motion
***************************************************************/
template<typename T>
Motion<T>::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<typename T>
Motion<T>::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<typename T>
Motion<T>::~Motion()
{
}
template<typename T>
void Motion<T>::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<typename T>
void Motion<T>::finish()
{
m_value = m_target;
m_velocity = T();
}
/*************************************************************** /***************************************************************
EffectWindow EffectWindow
***************************************************************/ ***************************************************************/

View file

@ -1,13 +1,12 @@
####################################### #######################################
# Effect # Effect
# Source files kwin_add_builtin_effect(slideback)
set(slideback_SOURCES target_sources(slideback PRIVATE
main.cpp main.cpp
motionmanager.cpp
slideback.cpp slideback.cpp
) )
kwin_add_builtin_effect(slideback ${slideback_SOURCES})
target_link_libraries(slideback PRIVATE target_link_libraries(slideback PRIVATE
kwin kwin
) )

View file

@ -0,0 +1,284 @@
/*
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
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<double>(initial, strength, smoothness)
{
}
Motion1D::Motion1D(const Motion1D &other)
: Motion<double>(other)
{
}
Motion1D::~Motion1D()
{
}
/***************************************************************
Motion2D
***************************************************************/
Motion2D::Motion2D(QPointF initial, double strength, double smoothness)
: Motion<QPointF>(initial, strength, smoothness)
{
}
Motion2D::Motion2D(const Motion2D &other)
: Motion<QPointF>(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<EffectWindow *, WindowMotion>::iterator it = m_managedWindows.begin();
for (; it != m_managedWindows.end(); ++it) {
WindowMotion *motion = &it.value();
motion->translation.finish();
motion->scale.finish();
}
}
QHash<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::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<EffectWindow *, WindowMotion>::ConstIterator it = m_managedWindows.constBegin();
while (it != m_managedWindows.constEnd()) {
if (exclusiveContains(transformedGeometry(it.key()), point)) {
return it.key();
}
++it;
}
return nullptr;
}
} // namespace KWin

View file

@ -0,0 +1,361 @@
/*
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "libkwineffects/effects.h"
namespace KWin
{
/**
* @internal
*/
template<typename T>
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<T> &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<double>
{
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<QPointF>
{
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<EffectWindow *, WindowMotion> m_managedWindows;
QSet<EffectWindow *> m_movingWindowsSet;
};
template<typename T>
Motion<T>::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<typename T>
Motion<T>::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<typename T>
Motion<T>::~Motion()
{
}
template<typename T>
void Motion<T>::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<typename T>
void Motion<T>::finish()
{
m_value = m_target;
m_velocity = T();
}
} // namespace KWin

View file

@ -11,6 +11,7 @@
// Include with base class for effects. // Include with base class for effects.
#include "libkwineffects/effects.h" #include "libkwineffects/effects.h"
#include "plugins/slideback/motionmanager.h"
namespace KWin namespace KWin
{ {