2016-09-28 10:15:03 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-09-28 10:15:03 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-09-28 10:15:03 +00:00
|
|
|
*/
|
|
|
|
#include "ui/effects/radial_animation.h"
|
|
|
|
|
2018-05-03 15:05:18 +00:00
|
|
|
#include "styles/style_widgets.h"
|
|
|
|
|
2016-09-28 10:15:03 +00:00
|
|
|
namespace Ui {
|
|
|
|
|
|
|
|
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
|
2018-05-03 15:05:18 +00:00
|
|
|
: a_arcStart(0, FullArcLength)
|
|
|
|
, _animation(std::move(callbacks)) {
|
2016-09-28 10:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RadialAnimation::start(float64 prg) {
|
|
|
|
_firstStart = _lastStart = _lastTime = getms();
|
|
|
|
int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
|
2016-12-05 11:01:08 +00:00
|
|
|
a_arcEnd = anim::value(iprgstrict, iprg);
|
2016-09-28 10:15:03 +00:00
|
|
|
_animation.start();
|
|
|
|
}
|
|
|
|
|
2018-09-20 16:39:59 +00:00
|
|
|
bool RadialAnimation::update(float64 prg, bool finished, TimeMs ms) {
|
|
|
|
const auto iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
|
|
|
|
const auto result = (iprg != qRound(a_arcEnd.to()));
|
2018-10-31 09:32:47 +00:00
|
|
|
if (_finished != finished) {
|
|
|
|
a_arcEnd.start(iprg);
|
|
|
|
_finished = finished;
|
|
|
|
_lastStart = _lastTime;
|
|
|
|
} else if (result) {
|
2016-09-28 10:15:03 +00:00
|
|
|
a_arcEnd.start(iprg);
|
|
|
|
_lastStart = _lastTime;
|
|
|
|
}
|
|
|
|
_lastTime = ms;
|
|
|
|
|
2018-10-31 09:32:47 +00:00
|
|
|
const auto dt = float64(ms - _lastStart);
|
|
|
|
const auto fulldt = float64(ms - _firstStart);
|
|
|
|
const auto opacitydt = _finished
|
|
|
|
? (_lastStart - _firstStart)
|
|
|
|
: fulldt;
|
|
|
|
_opacity = qMin(opacitydt / st::radialDuration, 1.);
|
2018-09-20 16:39:59 +00:00
|
|
|
if (anim::Disabled()) {
|
|
|
|
a_arcEnd.update(1., anim::linear);
|
|
|
|
if (finished) {
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
} else if (!finished) {
|
2016-09-28 10:15:03 +00:00
|
|
|
a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
|
|
|
|
} else if (dt >= st::radialDuration) {
|
2016-12-05 11:01:08 +00:00
|
|
|
a_arcEnd.update(1., anim::linear);
|
2016-09-28 10:15:03 +00:00
|
|
|
stop();
|
|
|
|
} else {
|
2016-12-05 11:01:08 +00:00
|
|
|
auto r = dt / st::radialDuration;
|
2016-09-28 10:15:03 +00:00
|
|
|
a_arcEnd.update(r, anim::linear);
|
|
|
|
_opacity *= 1 - r;
|
|
|
|
}
|
2016-12-05 11:01:08 +00:00
|
|
|
auto fromstart = fulldt / st::radialPeriod;
|
2016-09-28 10:15:03 +00:00
|
|
|
a_arcStart.update(fromstart - std::floor(fromstart), anim::linear);
|
2018-09-20 16:39:59 +00:00
|
|
|
return result;
|
2016-09-28 10:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RadialAnimation::stop() {
|
|
|
|
_firstStart = _lastStart = _lastTime = 0;
|
2016-12-05 11:01:08 +00:00
|
|
|
a_arcEnd = anim::value();
|
2016-09-28 10:15:03 +00:00
|
|
|
_animation.stop();
|
|
|
|
}
|
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
void RadialAnimation::step(TimeMs ms) {
|
2016-09-28 10:15:03 +00:00
|
|
|
_animation.step(ms);
|
|
|
|
}
|
|
|
|
|
2016-12-23 13:21:01 +00:00
|
|
|
void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, style::color color) {
|
2017-05-18 17:20:07 +00:00
|
|
|
auto o = p.opacity();
|
2016-09-28 10:15:03 +00:00
|
|
|
p.setOpacity(o * _opacity);
|
|
|
|
|
2017-05-18 17:20:07 +00:00
|
|
|
auto pen = color->p;
|
|
|
|
auto was = p.pen();
|
2016-09-28 10:15:03 +00:00
|
|
|
pen.setWidth(thickness);
|
2016-11-28 13:42:37 +00:00
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
2016-09-28 10:15:03 +00:00
|
|
|
p.setPen(pen);
|
|
|
|
|
2016-12-05 11:01:08 +00:00
|
|
|
auto len = MinArcLength + qRound(a_arcEnd.current());
|
2018-09-20 16:39:59 +00:00
|
|
|
auto from = QuarterArcLength
|
|
|
|
- len
|
|
|
|
- (anim::Disabled() ? 0 : qRound(a_arcStart.current()));
|
2016-09-28 10:15:03 +00:00
|
|
|
if (rtl()) {
|
|
|
|
from = QuarterArcLength - (from - QuarterArcLength) - len;
|
|
|
|
if (from < 0) from += FullArcLength;
|
|
|
|
}
|
|
|
|
|
2016-12-03 12:10:35 +00:00
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawArc(inner, from, len);
|
|
|
|
}
|
2016-09-28 10:15:03 +00:00
|
|
|
|
|
|
|
p.setPen(was);
|
|
|
|
p.setOpacity(o);
|
|
|
|
}
|
|
|
|
|
2018-05-04 16:57:50 +00:00
|
|
|
InfiniteRadialAnimation::InfiniteRadialAnimation(
|
|
|
|
AnimationCallbacks &&callbacks,
|
|
|
|
const style::InfiniteRadialAnimation &st)
|
|
|
|
: _st(st)
|
|
|
|
, _animation(std::move(callbacks)) {
|
2018-05-03 15:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InfiniteRadialAnimation::start() {
|
2018-05-04 16:57:50 +00:00
|
|
|
const auto now = getms();
|
2018-05-04 18:28:26 +00:00
|
|
|
if (_workFinished <= now && (_workFinished || !_workStarted)) {
|
2018-05-04 16:57:50 +00:00
|
|
|
_workStarted = now + _st.sineDuration;
|
|
|
|
_workFinished = 0;
|
2018-05-03 15:05:18 +00:00
|
|
|
}
|
2018-05-04 16:57:50 +00:00
|
|
|
if (!_animation.animating()) {
|
|
|
|
_animation.start();
|
2018-05-03 15:05:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InfiniteRadialAnimation::stop() {
|
2018-05-04 16:57:50 +00:00
|
|
|
const auto now = getms();
|
2018-09-20 16:39:59 +00:00
|
|
|
if (anim::Disabled()) {
|
|
|
|
_workFinished = now;
|
|
|
|
}
|
2018-05-04 16:57:50 +00:00
|
|
|
if (!_workFinished) {
|
|
|
|
const auto zero = _workStarted - _st.sineDuration;
|
|
|
|
const auto index = (now - zero + _st.sinePeriod - _st.sineShift)
|
|
|
|
/ _st.sinePeriod;
|
|
|
|
_workFinished = zero
|
|
|
|
+ _st.sineShift
|
|
|
|
+ (index * _st.sinePeriod)
|
|
|
|
+ _st.sineDuration;
|
|
|
|
} else if (_workFinished <= now) {
|
|
|
|
_animation.stop();
|
|
|
|
}
|
2018-05-03 15:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InfiniteRadialAnimation::step(TimeMs ms) {
|
|
|
|
_animation.step(ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InfiniteRadialAnimation::draw(
|
|
|
|
Painter &p,
|
|
|
|
QPoint position,
|
2018-05-04 16:57:50 +00:00
|
|
|
int outerWidth) {
|
2018-05-07 17:44:33 +00:00
|
|
|
draw(p, position, _st.size, outerWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InfiniteRadialAnimation::draw(
|
|
|
|
Painter &p,
|
|
|
|
QPoint position,
|
|
|
|
QSize size,
|
|
|
|
int outerWidth) {
|
2018-05-04 16:57:50 +00:00
|
|
|
const auto state = computeState();
|
|
|
|
|
2018-05-03 15:05:18 +00:00
|
|
|
auto o = p.opacity();
|
2018-05-04 16:57:50 +00:00
|
|
|
p.setOpacity(o * state.shown);
|
2018-05-03 15:05:18 +00:00
|
|
|
|
2018-09-20 16:39:59 +00:00
|
|
|
const auto rect = rtlrect(
|
|
|
|
position.x(),
|
|
|
|
position.y(),
|
|
|
|
size.width(),
|
|
|
|
size.height(),
|
|
|
|
outerWidth);
|
|
|
|
const auto was = p.pen();
|
|
|
|
const auto brush = p.brush();
|
|
|
|
if (anim::Disabled()) {
|
|
|
|
anim::DrawStaticLoading(p, rect, _st.thickness, _st.color);
|
|
|
|
} else {
|
|
|
|
auto pen = _st.color->p;
|
|
|
|
pen.setWidth(_st.thickness);
|
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
|
|
p.setPen(pen);
|
|
|
|
|
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.drawArc(
|
|
|
|
rect,
|
|
|
|
state.arcFrom,
|
|
|
|
state.arcLength);
|
|
|
|
}
|
2018-05-03 15:05:18 +00:00
|
|
|
}
|
|
|
|
p.setPen(was);
|
2018-09-20 16:39:59 +00:00
|
|
|
p.setBrush(brush);
|
2018-05-03 15:05:18 +00:00
|
|
|
p.setOpacity(o);
|
|
|
|
}
|
|
|
|
|
2018-05-04 16:57:50 +00:00
|
|
|
auto InfiniteRadialAnimation::computeState() -> State {
|
|
|
|
const auto now = getms();
|
|
|
|
const auto linear = int(((now * FullArcLength) / _st.linearPeriod)
|
|
|
|
% FullArcLength);
|
|
|
|
if (!_workStarted || (_workFinished && _workFinished <= now)) {
|
|
|
|
const auto shown = 0.;
|
|
|
|
_animation.stop();
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
linear,
|
|
|
|
FullArcLength };
|
|
|
|
}
|
2018-09-20 16:39:59 +00:00
|
|
|
if (anim::Disabled()) {
|
|
|
|
const auto shown = 1.;
|
|
|
|
return { 1., 0, FullArcLength };
|
|
|
|
}
|
2018-05-04 16:57:50 +00:00
|
|
|
const auto min = int(std::round(FullArcLength * _st.arcMin));
|
|
|
|
const auto max = int(std::round(FullArcLength * _st.arcMax));
|
|
|
|
if (now <= _workStarted) {
|
|
|
|
// zero .. _workStarted
|
|
|
|
const auto zero = _workStarted - _st.sineDuration;
|
|
|
|
const auto shown = (now - zero) / float64(_st.sineDuration);
|
|
|
|
const auto length = anim::interpolate(
|
|
|
|
FullArcLength,
|
|
|
|
min,
|
|
|
|
anim::sineInOut(1., snap(shown, 0., 1.)));
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
linear + (FullArcLength - length),
|
|
|
|
length };
|
|
|
|
} else if (!_workFinished || now <= _workFinished - _st.sineDuration) {
|
|
|
|
// _workStared .. _workFinished - _st.sineDuration
|
|
|
|
const auto shown = 1.;
|
|
|
|
const auto cycles = (now - _workStarted) / _st.sinePeriod;
|
|
|
|
const auto relative = (now - _workStarted) % _st.sinePeriod;
|
|
|
|
const auto smallDuration = _st.sineShift - _st.sineDuration;
|
|
|
|
const auto largeDuration = _st.sinePeriod
|
|
|
|
- _st.sineShift
|
|
|
|
- _st.sineDuration;
|
|
|
|
const auto basic = int((linear
|
|
|
|
+ (FullArcLength - min)
|
|
|
|
+ cycles * (max - min)) % FullArcLength);
|
|
|
|
if (relative <= smallDuration) {
|
|
|
|
// localZero .. growStart
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
basic,
|
|
|
|
min };
|
|
|
|
} else if (relative <= smallDuration + _st.sineDuration) {
|
|
|
|
// growStart .. growEnd
|
|
|
|
const auto growLinear = (relative - smallDuration) /
|
|
|
|
float64(_st.sineDuration);
|
|
|
|
const auto growProgress = anim::sineInOut(1., growLinear);
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
basic,
|
|
|
|
anim::interpolate(min, max, growProgress) };
|
|
|
|
} else if (relative <= _st.sinePeriod - _st.sineDuration) {
|
|
|
|
// growEnd .. shrinkStart
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
basic,
|
|
|
|
max };
|
|
|
|
} else {
|
|
|
|
// shrinkStart .. shrinkEnd
|
|
|
|
const auto shrinkLinear = (relative
|
|
|
|
- (_st.sinePeriod - _st.sineDuration))
|
|
|
|
/ float64(_st.sineDuration);
|
|
|
|
const auto shrinkProgress = anim::sineInOut(1., shrinkLinear);
|
|
|
|
const auto shrink = anim::interpolate(
|
|
|
|
0,
|
|
|
|
max - min,
|
|
|
|
shrinkProgress);
|
|
|
|
return {
|
|
|
|
shown,
|
|
|
|
basic + shrink,
|
|
|
|
max - shrink }; // interpolate(max, min, shrinkProgress)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// _workFinished - _st.sineDuration .. _workFinished
|
|
|
|
const auto hidden = (now - (_workFinished - _st.sineDuration))
|
|
|
|
/ float64(_st.sineDuration);
|
|
|
|
const auto cycles = (_workFinished - _workStarted) / _st.sinePeriod;
|
|
|
|
const auto basic = int((linear
|
|
|
|
+ (FullArcLength - min)
|
|
|
|
+ cycles * (max - min)) % FullArcLength);
|
|
|
|
const auto length = anim::interpolate(
|
|
|
|
min,
|
|
|
|
FullArcLength,
|
|
|
|
anim::sineInOut(1., snap(hidden, 0., 1.)));
|
|
|
|
return {
|
|
|
|
1. - hidden,
|
|
|
|
basic,
|
|
|
|
length };
|
|
|
|
}
|
|
|
|
//const auto frontPeriods = time / st.sinePeriod;
|
|
|
|
//const auto frontCurrent = time % st.sinePeriod;
|
|
|
|
//const auto frontProgress = anim::sineInOut(
|
|
|
|
// st.arcMax - st.arcMin,
|
|
|
|
// std::min(frontCurrent, TimeMs(st.sineDuration))
|
|
|
|
// / float64(st.sineDuration));
|
|
|
|
//const auto backTime = std::max(time - st.sineShift, 0LL);
|
|
|
|
//const auto backPeriods = backTime / st.sinePeriod;
|
|
|
|
//const auto backCurrent = backTime % st.sinePeriod;
|
|
|
|
//const auto backProgress = anim::sineInOut(
|
|
|
|
// st.arcMax - st.arcMin,
|
|
|
|
// std::min(backCurrent, TimeMs(st.sineDuration))
|
|
|
|
// / float64(st.sineDuration));
|
|
|
|
//const auto front = linear + std::round((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
|
|
|
//const auto from = linear + std::round((backProgress + backPeriods * (st.arcMax - st.arcMin)) * FullArcLength);
|
|
|
|
//const auto length = (front - from);
|
|
|
|
|
|
|
|
//return {
|
|
|
|
// _opacity,
|
|
|
|
// from,
|
|
|
|
// length
|
|
|
|
//};
|
|
|
|
}
|
|
|
|
|
2016-09-28 10:15:03 +00:00
|
|
|
} // namespace Ui
|