a281f2bce1
Summary: If a modal window is closed and some alternative effect that animates the disappearing of windows is enabled(e.g. the Glide effect, or the Scale effect), the Dialog Parent effect can cause flickering of the parent window because its animation duration doesn't match duration of those alternative effects. Also, if the Fade effect, the Glide effect, and the Scale effect are disabled, the Dialog Parent will keep the parent window alive for no good reason. This change addresses that problem by adding keepAlive property to `animate` function so scripted effects have more control over lifetime of animated windows. If both a modal window and its parent window are closed at the same time (and there is no effect that animates the disappearing of windows), the Dialog Parent will stop immediately(because windowDeleted will be emitted right after windowClosed signal). If both a modal window and its parent window are closed at the same time (and there is effect that animates the disappearing of windows), the Dialog Parent won't reference the latter window. Thus, it won't cause flickering. I.e. it will "passively" animate parent windows. BUG: 355036 FIXED-IN: 5.15.0 Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D14919
241 lines
11 KiB
C++
241 lines
11 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2011 Thomas Lübking <thomas.luebking@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
|
|
#ifndef ANIMATION_EFFECT_H
|
|
#define ANIMATION_EFFECT_H
|
|
|
|
#include <QEasingCurve>
|
|
#include <QElapsedTimer>
|
|
#include <qmath.h>
|
|
#include <kwineffects.h>
|
|
#include <kwineffects_export.h>
|
|
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
class KWINEFFECTS_EXPORT FPx2 {
|
|
public:
|
|
FPx2() { f[0] = f[1] = 0.0; valid = false; }
|
|
explicit FPx2(float v) { f[0] = f[1] = v; valid = true; }
|
|
FPx2(float v1, float v2) { f[0] = v1; f[1] = v2; valid = true; }
|
|
FPx2(const FPx2 &other) { f[0] = other.f[0]; f[1] = other.f[1]; valid = other.valid; }
|
|
explicit FPx2(const QPoint &other) { f[0] = other.x(); f[1] = other.y(); valid = true; }
|
|
explicit FPx2(const QPointF &other) { f[0] = other.x(); f[1] = other.y(); valid = true; }
|
|
explicit FPx2(const QSize &other) { f[0] = other.width(); f[1] = other.height(); valid = true; }
|
|
explicit FPx2(const QSizeF &other) { f[0] = other.width(); f[1] = other.height(); valid = true; }
|
|
inline void invalidate() { valid = false; }
|
|
inline bool isValid() const { return valid; }
|
|
inline float operator[](int n) const { return f[n]; }
|
|
inline QString toString() const {
|
|
QString ret;
|
|
if (valid)
|
|
ret = QString::number(f[0]) + QLatin1Char(',') + QString::number(f[1]);
|
|
else
|
|
ret = QString();
|
|
return ret;
|
|
}
|
|
|
|
|
|
inline FPx2 &operator+=(const FPx2 &other)
|
|
{ f[0] += other[0]; f[1] += other[1]; return *this; }
|
|
inline FPx2 &operator-=(const FPx2 &other)
|
|
{ f[0] -= other[0]; f[1] -= other[1]; return *this; }
|
|
inline FPx2 &operator*=(float fl)
|
|
{ f[0] *= fl; f[1] *= fl; return *this; }
|
|
inline FPx2 &operator/=(float fl)
|
|
{ f[0] /= fl; f[1] /= fl; return *this; }
|
|
|
|
friend inline bool operator==(const FPx2 &f1, const FPx2 &f2)
|
|
{ return f1[0] == f2[0] && f1[1] == f2[1]; }
|
|
friend inline bool operator!=(const FPx2 &f1, const FPx2 &f2)
|
|
{ return f1[0] != f2[0] || f1[1] != f2[1]; }
|
|
friend inline const FPx2 operator+(const FPx2 &f1, const FPx2 &f2)
|
|
{ return FPx2( f1[0] + f2[0], f1[1] + f2[1] ); }
|
|
friend inline const FPx2 operator-(const FPx2 &f1, const FPx2 &f2)
|
|
{ return FPx2( f1[0] - f2[0], f1[1] - f2[1] ); }
|
|
friend inline const FPx2 operator*(const FPx2 &f, float fl)
|
|
{ return FPx2( f[0] * fl, f[1] * fl ); }
|
|
friend inline const FPx2 operator*(float fl, const FPx2 &f)
|
|
{ return FPx2( f[0] * fl, f[1] *fl ); }
|
|
friend inline const FPx2 operator-(const FPx2 &f)
|
|
{ return FPx2( -f[0], -f[1] ); }
|
|
friend inline const FPx2 operator/(const FPx2 &f, float fl)
|
|
{ return FPx2( f[0] / fl, f[1] / fl ); }
|
|
|
|
inline void set(float v) { f[0] = v; valid = true; }
|
|
inline void set(float v1, float v2) { f[0] = v1; f[1] = v2; valid = true; }
|
|
|
|
private:
|
|
float f[2];
|
|
bool valid;
|
|
};
|
|
|
|
class AniData;
|
|
class AnimationEffectPrivate;
|
|
class KWINEFFECTS_EXPORT AnimationEffect : public Effect
|
|
{
|
|
Q_OBJECT
|
|
Q_ENUMS(Anchor)
|
|
Q_ENUMS(Attribute)
|
|
Q_ENUMS(MetaType)
|
|
public:
|
|
typedef QMap< EffectWindow*, QPair<QList<AniData>, QRect> > AniMap;
|
|
|
|
enum Anchor { Left = 1<<0, Top = 1<<1, Right = 1<<2, Bottom = 1<<3,
|
|
Horizontal = Left|Right, Vertical = Top|Bottom, Mouse = 1<<4 };
|
|
enum Attribute {
|
|
Opacity = 0, Brightness, Saturation, Scale, Rotation,
|
|
Position, Size, Translation, Clip, Generic, CrossFadePrevious,
|
|
NonFloatBase = Position
|
|
};
|
|
enum MetaType { SourceAnchor, TargetAnchor,
|
|
RelativeSourceX, RelativeSourceY, RelativeTargetX, RelativeTargetY, Axis };
|
|
/**
|
|
* Whenever you intend to connect to the EffectsHandler::windowClosed() signal, do so when reimplementing the constructor.
|
|
* Do *not* add private slots named _windowClosed( EffectWindow* w ) or _windowDeleted( EffectWindow* w ) !!
|
|
* The AnimationEffect connects them right *after* the construction.
|
|
* If you shadow the _windowDeleted slot (it doesn't matter that it's a private slot!), this will lead to segfaults.
|
|
* If you shadow _windowClosed() or connect your slot to EffectsHandler::windowClosed() after _windowClosed() was connected, animations for closing windows will fail.
|
|
*/
|
|
AnimationEffect();
|
|
~AnimationEffect();
|
|
|
|
bool isActive() const;
|
|
/**
|
|
* Set and get predefined metatypes.
|
|
* The first 24 bits are reserved for the AnimationEffect class - you can use the last 8 bits for custom hints.
|
|
* In case you transform a Generic attribute, all 32 bits are yours and you can use them as you want and read them in your genericAnimation() implementation.
|
|
*/
|
|
static int metaData(MetaType type, uint meta );
|
|
static void setMetaData(MetaType type, uint value, uint &meta );
|
|
|
|
/**
|
|
* Reimplemented from KWIn::Effect
|
|
*/
|
|
QString debug(const QString ¶meter) const;
|
|
virtual void prePaintScreen( ScreenPrePaintData& data, int time );
|
|
virtual void prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time );
|
|
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
|
|
virtual void postPaintScreen();
|
|
|
|
/**
|
|
* Gaussian (bumper) animation curve for QEasingCurve
|
|
*/
|
|
static qreal qecGaussian(qreal progress)
|
|
{
|
|
progress = 2*progress - 1;
|
|
progress *= -5*progress;
|
|
return qExp(progress);
|
|
}
|
|
|
|
static inline qint64 clock() {
|
|
return s_clock.elapsed();
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* The central function of this class - call it to create an animated transition of any supported attribute
|
|
* @param w - The EffectWindow to manipulate
|
|
* @param a - The @enum Attribute to manipulate
|
|
* @param meta - Basically a wildcard to carry various extra information, eg. the anchor, relativity or rotation axis. You will probably use require it when performing Generic animations.
|
|
* @param ms - How long the transition will last
|
|
* @param to - The target value. FPx2 is an agnostic two component float type (like QPointF or QSizeF, but without requiring to be either and supporting an invalid state)
|
|
* @param shape - How the animation progresses, eg. Linear progresses constantly while Exponential start slow and becomes very fast in the end
|
|
* @param delay - When the animation will start compared to "now" (the window will remain at the "from" position until then)
|
|
* @param from - The starting value, the default is invalid, ie. the attribute for the window is not transformed in the beginning
|
|
* @param fullScreen - Sets this effect as the active full screen effect for the duration of the animation
|
|
* @param keepAlive - Whether closed windows should be kept alive during animation
|
|
* @return an ID that you can use to cancel a running animation
|
|
*/
|
|
quint64 animate( EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, QEasingCurve curve = QEasingCurve(), int delay = 0, FPx2 from = FPx2(), bool fullScreen = false, bool keepAlive = true)
|
|
{ return p_animate(w, a, meta, ms, to, curve, delay, from, false, fullScreen, keepAlive); }
|
|
|
|
/**
|
|
* Equal to ::animate() with one important difference:
|
|
* The target value for the attribute is kept until you ::cancel() this animation
|
|
* @return an ID that you need to use to cancel this manipulation
|
|
*/
|
|
quint64 set( EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, QEasingCurve curve = QEasingCurve(), int delay = 0, FPx2 from = FPx2(), bool fullScreen = false, bool keepAlive = true)
|
|
{ return p_animate(w, a, meta, ms, to, curve, delay, from, true, fullScreen, keepAlive); }
|
|
|
|
/**
|
|
* this allows to alter the target (but not type or curve) of a running animation
|
|
* with the ID @param animationId
|
|
* @param newTarget alters the "to" parameter of the animation
|
|
* If @param newRemainingTime allows to lengthen (or shorten) the remaining time
|
|
* of the animation. By default (-1) the remaining time remains unchanged
|
|
*
|
|
* Please use @function cancel to cancel an animation rather than altering it.
|
|
* NOTICE that you can NOT retarget an animation that just has just @function animationEnded !
|
|
* @return whether there was such animation and it could be altered
|
|
*/
|
|
bool retarget(quint64 animationId, FPx2 newTarget, int newRemainingTime = -1);
|
|
|
|
/**
|
|
* Called whenever an animation end, passes the transformed @class EffectWindow @enum Attribute and originally supplied @param meta
|
|
* You can reimplement it to keep a constant transformation for the window (ie. keep it a this opacity or position) or to start another animation
|
|
*/
|
|
virtual void animationEnded( EffectWindow *, Attribute, uint meta ) {Q_UNUSED(meta);}
|
|
|
|
/**
|
|
* Cancel a running animation. @return true if an animation for @p animationId was found (and canceled)
|
|
* NOTICE that there is NO animated reset of the original value. You'll have to provide that with a second animation
|
|
* NOTICE as well that this will eventually release a Deleted window.
|
|
* If you intend to run another animation on the (Deleted) window, you have to do that before cancelling the old animation (to keep the window around)
|
|
*/
|
|
bool cancel(quint64 animationId);
|
|
/**
|
|
* Called if the transformed @enum Attribute is Generic. You should reimplement it if you transform this "Attribute".
|
|
* You could use the meta information to eg. support more than one additional animations
|
|
*/
|
|
virtual void genericAnimation( EffectWindow *w, WindowPaintData &data, float progress, uint meta )
|
|
{Q_UNUSED(w); Q_UNUSED(data); Q_UNUSED(progress); Q_UNUSED(meta);}
|
|
|
|
//Internal for unit tests
|
|
AniMap state() const;
|
|
|
|
private:
|
|
quint64 p_animate(EffectWindow *w, Attribute a, uint meta, int ms, FPx2 to, QEasingCurve curve, int delay, FPx2 from, bool keepAtTarget, bool fullScreenEffect, bool keepAlive);
|
|
QRect clipRect(const QRect &windowRect, const AniData&) const;
|
|
void clipWindow(const EffectWindow *, const AniData &, WindowQuadList &) const;
|
|
float interpolated( const AniData&, int i = 0 ) const;
|
|
float progress( const AniData& ) const;
|
|
void disconnectGeometryChanges();
|
|
void updateLayerRepaints();
|
|
void validate(Attribute a, uint &meta, FPx2 *from, FPx2 *to, const EffectWindow *w) const;
|
|
private Q_SLOTS:
|
|
void init();
|
|
void triggerRepaint();
|
|
void _windowClosed( KWin::EffectWindow* w );
|
|
void _windowDeleted( KWin::EffectWindow* w );
|
|
void _expandedGeometryChanged(KWin::EffectWindow *w, const QRect &old);
|
|
private:
|
|
static QElapsedTimer s_clock;
|
|
AnimationEffectPrivate * const d_ptr;
|
|
Q_DECLARE_PRIVATE(AnimationEffect)
|
|
};
|
|
|
|
|
|
} // namespace
|
|
QDebug operator<<(QDebug dbg, const KWin::FPx2 &fpx2);
|
|
Q_DECLARE_METATYPE(KWin::FPx2)
|
|
|
|
#endif // ANIMATION_EFFECT_H
|