Fix ripple glitch on theme switching.

This commit is contained in:
John Preston 2018-10-25 14:02:37 +04:00
parent eb099c70e6
commit 9aa23dac80
4 changed files with 56 additions and 33 deletions

View File

@ -11,21 +11,22 @@ namespace Ui {
class RippleAnimation::Ripple { class RippleAnimation::Ripple {
public: public:
Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, const UpdateCallback &update); Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update);
Ripple(const style::RippleAnimation &st, const QPixmap &mask, const UpdateCallback &update); Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn<void()> update);
void paint(QPainter &p, const QPixmap &mask, TimeMs ms, const QColor *colorOverride); void paint(QPainter &p, const QPixmap &mask, TimeMs ms, const QColor *colorOverride);
void stop(); void stop();
void unstop(); void unstop();
void finish(); void finish();
void clearCache();
bool finished() const { bool finished() const {
return _hiding && !_hide.animating(); return _hiding && !_hide.animating();
} }
private: private:
const style::RippleAnimation &_st; const style::RippleAnimation &_st;
UpdateCallback _update; Fn<void()> _update;
QPoint _origin; QPoint _origin;
int _radiusFrom = 0; int _radiusFrom = 0;
@ -39,7 +40,7 @@ private:
}; };
RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, const UpdateCallback &update) RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update)
: _st(st) : _st(st)
, _update(update) , _update(update)
, _origin(origin) , _origin(origin)
@ -58,10 +59,10 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin,
} }
_radiusTo = qRound(sqrt(_radiusTo)); _radiusTo = qRound(sqrt(_radiusTo));
_show.start(UpdateCallback(_update), 0., 1., _st.showDuration, anim::easeOutQuint); _show.start(_update, 0., 1., _st.showDuration, anim::easeOutQuint);
} }
RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, const UpdateCallback &update) RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn<void()> update)
: _st(st) : _st(st)
, _update(update) , _update(update)
, _origin(mask.width() / (2 * cIntRetinaFactor()), mask.height() / (2 * cIntRetinaFactor())) , _origin(mask.width() / (2 * cIntRetinaFactor()), mask.height() / (2 * cIntRetinaFactor()))
@ -69,7 +70,7 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap
, _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) { , _frame(mask.size(), QImage::Format_ARGB32_Premultiplied) {
_frame.setDevicePixelRatio(mask.devicePixelRatio()); _frame.setDevicePixelRatio(mask.devicePixelRatio());
_radiusTo = _radiusFrom; _radiusTo = _radiusFrom;
_hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration); _hide.start(_update, 0., 1., _st.hideDuration);
} }
void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, TimeMs ms, const QColor *colorOverride) { void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, TimeMs ms, const QColor *colorOverride) {
@ -112,13 +113,13 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, TimeMs ms,
void RippleAnimation::Ripple::stop() { void RippleAnimation::Ripple::stop() {
_hiding = true; _hiding = true;
_hide.start(UpdateCallback(_update), 1., 0., _st.hideDuration); _hide.start(_update, 1., 0., _st.hideDuration);
} }
void RippleAnimation::Ripple::unstop() { void RippleAnimation::Ripple::unstop() {
if (_hiding) { if (_hiding) {
if (_hide.animating()) { if (_hide.animating()) {
_hide.start(UpdateCallback(_update), 0., 1., _st.hideDuration); _hide.start(_update, 0., 1., _st.hideDuration);
} }
_hiding = false; _hiding = false;
} }
@ -132,7 +133,11 @@ void RippleAnimation::Ripple::finish() {
_hide.finish(); _hide.finish();
} }
RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, const UpdateCallback &callback) void RippleAnimation::Ripple::clearCache() {
_cache = QPixmap();
}
RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> callback)
: _st(st) : _st(st)
, _mask(App::pixmapFromImageInPlace(std::move(mask))) , _mask(App::pixmapFromImageInPlace(std::move(mask)))
, _update(callback) { , _update(callback) {
@ -141,40 +146,49 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask,
void RippleAnimation::add(QPoint origin, int startRadius) { void RippleAnimation::add(QPoint origin, int startRadius) {
lastStop(); lastStop();
_ripples.push_back(new Ripple(_st, origin, startRadius, _mask, _update)); _ripples.push_back(std::make_unique<Ripple>(_st, origin, startRadius, _mask, _update));
} }
void RippleAnimation::addFading() { void RippleAnimation::addFading() {
lastStop(); lastStop();
_ripples.push_back(new Ripple(_st, _mask, _update)); _ripples.push_back(std::make_unique<Ripple>(_st, _mask, _update));
} }
void RippleAnimation::lastStop() { void RippleAnimation::lastStop() {
if (!_ripples.isEmpty()) { if (!_ripples.empty()) {
_ripples.back()->stop(); _ripples.back()->stop();
} }
} }
void RippleAnimation::lastUnstop() { void RippleAnimation::lastUnstop() {
if (!_ripples.isEmpty()) { if (!_ripples.empty()) {
_ripples.back()->unstop(); _ripples.back()->unstop();
} }
} }
void RippleAnimation::lastFinish() { void RippleAnimation::lastFinish() {
if (!_ripples.isEmpty()) { if (!_ripples.empty()) {
_ripples.back()->finish(); _ripples.back()->finish();
} }
} }
void RippleAnimation::forceRepaint() {
for (const auto &ripple : _ripples) {
ripple->clearCache();
}
if (_update) {
_update();
}
}
void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride) { void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride) {
if (_ripples.isEmpty()) { if (_ripples.empty()) {
return; return;
} }
if (rtl()) x = outerWidth - x - (_mask.width() / cIntRetinaFactor()); if (rtl()) x = outerWidth - x - (_mask.width() / cIntRetinaFactor());
p.translate(x, y); p.translate(x, y);
for (auto ripple : _ripples) { for (const auto &ripple : _ripples) {
ripple->paint(p, _mask, ms, colorOverride); ripple->paint(p, _mask, ms, colorOverride);
} }
p.translate(-x, -y); p.translate(-x, -y);
@ -213,16 +227,15 @@ QImage RippleAnimation::ellipseMask(QSize size) {
} }
void RippleAnimation::clearFinished() { void RippleAnimation::clearFinished() {
while (!_ripples.isEmpty() && _ripples.front()->finished()) { while (!_ripples.empty() && _ripples.front()->finished()) {
delete base::take(_ripples.front());
_ripples.pop_front(); _ripples.pop_front();
} }
} }
void RippleAnimation::clear() { void RippleAnimation::clear() {
for (auto ripple : base::take(_ripples)) { _ripples.clear();
delete ripple;
}
} }
RippleAnimation::~RippleAnimation() = default;
} // namespace Ui } // namespace Ui

View File

@ -13,21 +13,20 @@ namespace Ui {
class RippleAnimation { class RippleAnimation {
public: public:
using UpdateCallback = Fn<void()>;
// White upon transparent mask, like colorizeImage(black-white-mask, white). // White upon transparent mask, like colorizeImage(black-white-mask, white).
RippleAnimation(const style::RippleAnimation &st, QImage mask, const UpdateCallback &update); RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> update);
void add(QPoint origin, int startRadius = 0); void add(QPoint origin, int startRadius = 0);
void addFading(); void addFading();
void lastStop(); void lastStop();
void lastUnstop(); void lastUnstop();
void lastFinish(); void lastFinish();
void forceRepaint();
void paint(QPainter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride = nullptr); void paint(QPainter &p, int x, int y, int outerWidth, TimeMs ms, const QColor *colorOverride = nullptr);
bool empty() const { bool empty() const {
return _ripples.isEmpty(); return _ripples.empty();
} }
static QImage maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer); static QImage maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer);
@ -35,9 +34,7 @@ public:
static QImage roundRectMask(QSize size, int radius); static QImage roundRectMask(QSize size, int radius);
static QImage ellipseMask(QSize size); static QImage ellipseMask(QSize size);
~RippleAnimation() { ~RippleAnimation();
clear();
}
private: private:
void clear(); void clear();
@ -45,10 +42,10 @@ private:
const style::RippleAnimation &_st; const style::RippleAnimation &_st;
QPixmap _mask; QPixmap _mask;
UpdateCallback _update; Fn<void()> _update;
class Ripple; class Ripple;
QList<Ripple*> _ripples; std::deque<std::unique_ptr<Ripple>> _ripples;
}; };

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/cross_animation.h" #include "ui/effects/cross_animation.h"
#include "ui/effects/numbers_animation.h" #include "ui/effects/numbers_animation.h"
#include "ui/image/image_prepare.h" #include "ui/image/image_prepare.h"
#include "window/themes/window_theme.h"
#include "lang/lang_instance.h" #include "lang/lang_instance.h"
namespace Ui { namespace Ui {
@ -89,15 +90,26 @@ void RippleButton::setForceRippled(
if (_forceRippled != rippled) { if (_forceRippled != rippled) {
_forceRippled = rippled; _forceRippled = rippled;
if (_forceRippled) { if (_forceRippled) {
_forceRippledSubscription = base::ObservableViewer(
*Window::Theme::Background()
) | rpl::start_with_next([=](
const Window::Theme::BackgroundUpdate &update) {
if (update.paletteChanged() && _ripple) {
_ripple->forceRepaint();
}
});
ensureRipple(); ensureRipple();
if (_ripple->empty()) { if (_ripple->empty()) {
_ripple->addFading(); _ripple->addFading();
} else { } else {
_ripple->lastUnstop(); _ripple->lastUnstop();
} }
} else if (_ripple) { } else {
if (_ripple) {
_ripple->lastStop(); _ripple->lastStop();
} }
_forceRippledSubscription.destroy();
}
} }
if (animated == anim::type::instant && _ripple) { if (animated == anim::type::instant && _ripple) {
_ripple->lastFinish(); _ripple->lastFinish();

View File

@ -73,6 +73,7 @@ private:
const style::RippleAnimation &_st; const style::RippleAnimation &_st;
std::unique_ptr<RippleAnimation> _ripple; std::unique_ptr<RippleAnimation> _ripple;
bool _forceRippled = false; bool _forceRippled = false;
rpl::lifetime _forceRippledSubscription;
}; };