radial_animation.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/effects/radial_animation.h"
  8. #include "ui/arc_angles.h"
  9. #include "ui/painter.h"
  10. #include "styles/style_widgets.h"
  11. namespace Ui {
  12. namespace {
  13. constexpr auto kFullArcLength = arc::kFullLength;
  14. } // namespace
  15. const int RadialState::kFull = kFullArcLength;
  16. void RadialAnimation::start(float64 prg) {
  17. _firstStart = _lastStart = _lastTime = crl::now();
  18. const auto iprg = qRound(qMax(prg, 0.0001) * arc::kAlmostFullLength);
  19. const auto iprgstrict = qRound(prg * arc::kAlmostFullLength);
  20. _arcEnd = anim::value(iprgstrict, iprg);
  21. _animation.start();
  22. }
  23. bool RadialAnimation::update(float64 prg, bool finished, crl::time ms) {
  24. const auto iprg = qRound(qMax(prg, 0.0001) * arc::kAlmostFullLength);
  25. const auto result = (iprg != qRound(_arcEnd.to()))
  26. || (_finished != finished);
  27. if (_finished != finished) {
  28. _arcEnd.start(iprg);
  29. _finished = finished;
  30. _lastStart = _lastTime;
  31. } else if (result) {
  32. _arcEnd.start(iprg);
  33. _lastStart = _lastTime;
  34. }
  35. _lastTime = ms;
  36. const auto dt = float64(ms - _lastStart);
  37. const auto fulldt = float64(ms - _firstStart);
  38. const auto opacitydt = _finished
  39. ? (_lastStart - _firstStart)
  40. : fulldt;
  41. _opacity = qMin(opacitydt / st::radialDuration, 1.);
  42. if (anim::Disabled()) {
  43. _arcEnd.update(1., anim::linear);
  44. if (finished) {
  45. stop();
  46. }
  47. } else if (!finished) {
  48. _arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
  49. } else if (dt >= st::radialDuration) {
  50. _arcEnd.update(1., anim::linear);
  51. stop();
  52. } else {
  53. auto r = dt / st::radialDuration;
  54. _arcEnd.update(r, anim::linear);
  55. _opacity *= 1 - r;
  56. }
  57. auto fromstart = fulldt / st::radialPeriod;
  58. _arcStart.update(fromstart - std::floor(fromstart), anim::linear);
  59. return result;
  60. }
  61. void RadialAnimation::stop() {
  62. _firstStart = _lastStart = _lastTime = 0;
  63. _arcEnd = anim::value();
  64. _animation.stop();
  65. }
  66. void RadialAnimation::draw(
  67. QPainter &p,
  68. const QRectF &inner,
  69. float64 thickness,
  70. style::color color) const {
  71. const auto state = computeState();
  72. auto o = p.opacity();
  73. p.setOpacity(o * state.shown);
  74. auto pen = color->p;
  75. auto was = p.pen();
  76. pen.setWidthF(thickness);
  77. pen.setCapStyle(Qt::RoundCap);
  78. p.setPen(pen);
  79. {
  80. PainterHighQualityEnabler hq(p);
  81. p.drawArc(inner, state.arcFrom, state.arcLength);
  82. }
  83. p.setPen(was);
  84. p.setOpacity(o);
  85. }
  86. RadialState RadialAnimation::computeState() const {
  87. auto length = arc::kMinLength + qRound(_arcEnd.current());
  88. auto from = arc::kQuarterLength
  89. - length
  90. - (anim::Disabled() ? 0 : qRound(_arcStart.current()));
  91. if (style::RightToLeft()) {
  92. from = arc::kQuarterLength - (from - arc::kQuarterLength) - length;
  93. if (from < 0) from += arc::kFullLength;
  94. }
  95. return { _opacity, from, length };
  96. }
  97. void InfiniteRadialAnimation::init() {
  98. anim::Disables() | rpl::filter([=] {
  99. return animating();
  100. }) | rpl::start_with_next([=](bool disabled) {
  101. if (!disabled && !_animation.animating()) {
  102. _animation.start();
  103. } else if (disabled && _animation.animating()) {
  104. _animation.stop();
  105. }
  106. }, _lifetime);
  107. }
  108. void InfiniteRadialAnimation::start(crl::time skip) {
  109. if (!animating()) {
  110. const auto now = crl::now();
  111. _workStarted = std::max(now + _st.sineDuration - skip, crl::time(1));
  112. _workFinished = 0;
  113. }
  114. if (!anim::Disabled() && !_animation.animating()) {
  115. _animation.start();
  116. }
  117. }
  118. void InfiniteRadialAnimation::stop(anim::type animated) {
  119. const auto now = crl::now();
  120. if (anim::Disabled() || animated == anim::type::instant) {
  121. _workFinished = now;
  122. }
  123. if (!_workFinished) {
  124. const auto zero = _workStarted - _st.sineDuration;
  125. const auto index = (now - zero + _st.sinePeriod - _st.sineShift)
  126. / _st.sinePeriod;
  127. _workFinished = zero
  128. + _st.sineShift
  129. + (index * _st.sinePeriod)
  130. + _st.sineDuration;
  131. } else if (_workFinished <= now) {
  132. _animation.stop();
  133. }
  134. }
  135. void InfiniteRadialAnimation::draw(
  136. QPainter &p,
  137. QPoint position,
  138. int outerWidth) {
  139. Draw(
  140. p,
  141. computeState(),
  142. position,
  143. _st.size,
  144. outerWidth,
  145. _st.color,
  146. _st.thickness);
  147. }
  148. void InfiniteRadialAnimation::draw(
  149. QPainter &p,
  150. QPoint position,
  151. QSize size,
  152. int outerWidth) {
  153. Draw(
  154. p,
  155. computeState(),
  156. position,
  157. size,
  158. outerWidth,
  159. _st.color,
  160. _st.thickness);
  161. }
  162. void InfiniteRadialAnimation::Draw(
  163. QPainter &p,
  164. const RadialState &state,
  165. QPoint position,
  166. QSize size,
  167. int outerWidth,
  168. QPen pen,
  169. int thickness) {
  170. auto o = p.opacity();
  171. p.setOpacity(o * state.shown);
  172. const auto rect = style::rtlrect(
  173. position.x(),
  174. position.y(),
  175. size.width(),
  176. size.height(),
  177. outerWidth);
  178. const auto was = p.pen();
  179. const auto brush = p.brush();
  180. if (anim::Disabled()) {
  181. anim::DrawStaticLoading(p, rect, thickness, pen);
  182. } else {
  183. pen.setWidth(thickness);
  184. pen.setCapStyle(Qt::RoundCap);
  185. p.setPen(pen);
  186. {
  187. PainterHighQualityEnabler hq(p);
  188. p.drawArc(
  189. rect,
  190. state.arcFrom,
  191. state.arcLength);
  192. }
  193. }
  194. p.setPen(was);
  195. p.setBrush(brush);
  196. p.setOpacity(o);
  197. }
  198. RadialState InfiniteRadialAnimation::computeState() {
  199. const auto now = crl::now();
  200. const auto linear = kFullArcLength
  201. - int(((now * kFullArcLength) / _st.linearPeriod) % kFullArcLength);
  202. if (!animating()) {
  203. const auto shown = 0.;
  204. _animation.stop();
  205. return {
  206. shown,
  207. linear,
  208. kFullArcLength };
  209. }
  210. if (anim::Disabled()) {
  211. return { 1., 0, kFullArcLength };
  212. }
  213. const auto min = int(base::SafeRound(kFullArcLength * _st.arcMin));
  214. const auto max = int(base::SafeRound(kFullArcLength * _st.arcMax));
  215. if (now <= _workStarted) {
  216. // zero .. _workStarted
  217. const auto zero = _workStarted - _st.sineDuration;
  218. const auto shown = (now - zero) / float64(_st.sineDuration);
  219. const auto length = anim::interpolate(
  220. kFullArcLength,
  221. min,
  222. anim::sineInOut(1., std::clamp(shown, 0., 1.)));
  223. return {
  224. shown,
  225. linear,
  226. length };
  227. } else if (!_workFinished || now <= _workFinished - _st.sineDuration) {
  228. // _workStared .. _workFinished - _st.sineDuration
  229. const auto shown = 1.;
  230. const auto cycles = (now - _workStarted) / _st.sinePeriod;
  231. const auto relative = (now - _workStarted) % _st.sinePeriod;
  232. const auto smallDuration = _st.sineShift - _st.sineDuration;
  233. const auto basic = int((linear
  234. + min
  235. + (cycles * (kFullArcLength + min - max))) % kFullArcLength);
  236. if (relative <= smallDuration) {
  237. // localZero .. growStart
  238. return {
  239. shown,
  240. basic - min,
  241. min };
  242. } else if (relative <= smallDuration + _st.sineDuration) {
  243. // growStart .. growEnd
  244. const auto growLinear = (relative - smallDuration) /
  245. float64(_st.sineDuration);
  246. const auto growProgress = anim::sineInOut(1., growLinear);
  247. const auto length = anim::interpolate(min, max, growProgress);
  248. return {
  249. shown,
  250. basic - length,
  251. length };
  252. } else if (relative <= _st.sinePeriod - _st.sineDuration) {
  253. // growEnd .. shrinkStart
  254. return {
  255. shown,
  256. basic - max,
  257. max };
  258. } else {
  259. // shrinkStart .. shrinkEnd
  260. const auto shrinkLinear = (relative
  261. - (_st.sinePeriod - _st.sineDuration))
  262. / float64(_st.sineDuration);
  263. const auto shrinkProgress = anim::sineInOut(1., shrinkLinear);
  264. const auto shrink = anim::interpolate(
  265. 0,
  266. max - min,
  267. shrinkProgress);
  268. return {
  269. shown,
  270. basic - max,
  271. max - shrink }; // interpolate(max, min, shrinkProgress)
  272. }
  273. } else {
  274. // _workFinished - _st.sineDuration .. _workFinished
  275. const auto hidden = (now - (_workFinished - _st.sineDuration))
  276. / float64(_st.sineDuration);
  277. const auto cycles = (_workFinished - _workStarted) / _st.sinePeriod;
  278. const auto basic = int((linear
  279. + min
  280. + cycles * (kFullArcLength + min - max)) % kFullArcLength);
  281. const auto length = anim::interpolate(
  282. min,
  283. kFullArcLength,
  284. anim::sineInOut(1., std::clamp(hidden, 0., 1.)));
  285. return {
  286. 1. - hidden,
  287. basic - length,
  288. length };
  289. }
  290. //const auto frontPeriods = time / st.sinePeriod;
  291. //const auto frontCurrent = time % st.sinePeriod;
  292. //const auto frontProgress = anim::sineInOut(
  293. // st.arcMax - st.arcMin,
  294. // std::min(frontCurrent, crl::time(st.sineDuration))
  295. // / float64(st.sineDuration));
  296. //const auto backTime = std::max(time - st.sineShift, 0LL);
  297. //const auto backPeriods = backTime / st.sinePeriod;
  298. //const auto backCurrent = backTime % st.sinePeriod;
  299. //const auto backProgress = anim::sineInOut(
  300. // st.arcMax - st.arcMin,
  301. // std::min(backCurrent, crl::time(st.sineDuration))
  302. // / float64(st.sineDuration));
  303. //const auto front = linear + base::SafeRound((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * kFullArcLength);
  304. //const auto from = linear + base::SafeRound((backProgress + backPeriods * (st.arcMax - st.arcMin)) * kFullArcLength);
  305. //const auto length = (front - from);
  306. //return {
  307. // _opacity,
  308. // from,
  309. // length
  310. //};
  311. }
  312. } // namespace Ui